iOS 3D Touch - Peek and pop actions (Part 2)
This is the second part of the series of 3 posts on how to use 3D force touch on an iOS. Posts are as follows
- Quick actions
- Peek and pop actions
- Measuring the force of touch
To give you a glimpse of this demo, let's begin with a little demo presentation.
In this demo we are going to create a list of former US presidents and use 3D touch peek and pop actions to get more information about them.
Let's begin with creating object model for President,
struct JKPresidentInfo {
let name: String
let birthDate: String
let deathDate: String
let birthPlace: String
}
// Now let's make an array holding objects of type JKPresidentInfo
var listOfPresidents: [JKPresidentInfo] = [JKPresidentInfo(name: "Lincoln", birthDate: "02/1809", deathDate: "04/1865", birthPlace: "Hardin County"), JKPresidentInfo(name: "Roosevelt", birthDate: "01/1882", deathDate: "04/1945", birthPlace: "Hyde Park"), JKPresidentInfo(name: "Clinton", birthDate: "08/1946", deathDate: "N/A", birthPlace: "Hope"), JKPresidentInfo(name: "Obama", birthDate: "08/1961", deathDate: "N/A", birthPlace: "Honolulu")]
A thing to note before we begin,
- There is possibility that device may not be 3D touch enabled. In that case you will have to add alternate to 3D touch which includes but not limited to long press recognizer gesture.
lazy var longPress: UILongPressGestureRecognizer = {
let longPressGesture = UILongPressGestureRecognizer(target: self, action: #selector(handleLongPress))
return longPressGesture
}()
// Add it to self.view in case 3D touch is not enabled.
self.view.addGestureRecognizer(longPress)
This long press gesture can be added to self.view
in case device does not have 3D force touch capability or user toggles the 3D touch capabilities through settings. 3D touch capability can be detected in viewWillAppear
or by overriding traitCollectionDidChange
method. This method gets called every time user enables/disables 3D touch capabilities.
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
self.check3DTouchEnabledFlag()
}
override func traitCollectionDidChange(previousTraitCollection: UITraitCollection?) {
self.check3DTouchEnabledFlag()
}
func check3DTouchEnabledFlag() {
if self.traitCollection.forceTouchCapability == UIForceTouchCapability.Available {
self.registerForPreviewingWithDelegate(self, sourceView: self.view)
self.longPress.enabled = false
print("Force touch does exist")
} else {
self.longPress.enabled = true
print("Force touch does not exists on this device")
}
}
This list of presidents looks like this,
Now there are two actions attached to it.
-
When user lightly touches the president name
This action is used as a preview. On this screen user can briefly preview the president info and do additional actions without having to go to actual president details page.Here we will assume user can
like
the president. So we will present that options from the bottom of the page and user can select it. This can be implemented with following delegate method. In order to use it you will have to conform toUIViewControllerPreviewingDelegate
protocol.
// MARK: - 3D touch delegate method.
func previewingContext(previewingContext: UIViewControllerPreviewing, viewControllerForLocation location: CGPoint) -> UIViewController? {
if (self.presentedViewController?.isKindOfClass(JKLongPress3DPreviewViewController) == true) {
return nil
}
var updatedLocation = tableView.convertPoint(location, toView: self.view)
updatedLocation = CGPoint(x: location.x, y: location.y - 64)
if let indexPath = tableView.indexPathForRowAtPoint(updatedLocation) {
if let cell = tableView.cellForRowAtIndexPath(indexPath) {
tableView.selectRowAtIndexPath(indexPath, animated: false, scrollPosition: UITableViewScrollPosition.None)
previewingContext.sourceRect = CGRectMake(cell.frame.origin.x, cell.frame.origin.y + 64, cell.bounds.width, cell.bounds.height)
let president = listOfPresidents[indexPath.row]
let selectedPresidentPreviewViewController = JKLongPress3DPreviewViewController()
selectedPresidentPreviewViewController.selectedPresident = president
selectedPresidentPreviewViewController.presidentLiked = self.presidentLikedWithName(president.name)
selectedPresidentPreviewViewController.presidentFavoritedAction = { president in
if (self.presidentLikedWithName(president.name) == true) {
self.favoritedList.removeObject(president.name)
} else {
self.favoritedList.append(president.name)
}
self.tableView.reloadData()
}
return selectedPresidentPreviewViewController
}
}
return nil
}
Where JKLongPress3DPreviewViewController
is the preview controller where user can choose an additional actions.
You may customize this preview controller the way you want, but please be aware that this view appears as a foreground view fading the original view behind it. You can then provide any number of list actions on this page and specify which actions they should trigger. This can be done by overriding previewActionItems
method.
override func previewActionItems() -> [UIPreviewActionItem] {
let actionTitle = presidentLiked == true ? "Unlike": "Like"
let action1 = UIPreviewAction(title: actionTitle, style: .Default) { (action, controller) in
if let president = self.selectedPresident {
print("President \(president.name) was liked by the user")
if let presidentFavoritedBlock = self.presidentFavoritedAction {
presidentFavoritedBlock(president)
}
}
}
let action2 = UIPreviewAction(title: "Cancel", style: .Destructive) { (action, controller) in
print("Cancel Action Selected")
}
let actionGroup = UIPreviewActionGroup(title: "More Actions", style: .Default, actions: [action1, action2])
let finalArray = NSArray.init(object: actionGroup)
return finalArray as! [UIPreviewActionItem]
}
To explain the above code briefly, we override the method previewActionItems
to provide the list of UIPreviewActionItem
. We create two actions. One for liking the president photo and another for cancel operation. These action items allow us to specify which action should take place upon selecting that option.
- When user heavily touches the president's name from the list
// MARK: - 3D touch delegate method.
func previewingContext(previewingContext: UIViewControllerPreviewing, commitViewController viewControllerToCommit: UIViewController) {
if let previewVC = viewControllerToCommit as? JKLongPress3DPreviewViewController, selectedPresident = previewVC.selectedPresident {
if let destinationVC = JK3DTouchDestinationViewController(president: selectedPresident) {
self.showViewController(destinationVC, sender: self)
}
}
}
This action takes user directly to the page which shows detailed president info. This is same as user simply clicking the president name from the list.
In case where 3D touch is disabled/not available you can simply use long press gesture recognizer to detect light press and present appropriate preview controller when user can perform additional actions.
Note: The full project for 3D touch demo is Available on Github