One of iOS's lesser-used features is 3D Touch. 3D Touch allows users to make their intent clear by either tapping the screen or by pressing it a little more firmly. The iPhone 6s and newer devices have implemented this functionality and it allows for some pretty neat interactions. One of these is called peek and pop. With peek and pop, a user can Force Touch an element on the screen and they'll see a preview of the detail page they'd see if they had performed a regular tap on the UI element. The following screenshot shows an example of such a preview:
If the user presses a little bit more firmly than when they saw the preview appear, they commit to the preview, meaning they will navigate to the detail page. To implement this, we only need to perform a small amount of work. First of all, the overview view controller must register its desire to provide preview capabilities for its collection view. Second, the overview view controller should implement the UIViewControllerPreviewingDelegate protocol so it can provide the preview with the correct view controller and commit to the preview if needed.
Before we get to the code, we should make one minor change to the storyboard. Because the UIViewControllerPreviewingDelegate must be able to provide a view controller to preview, we need to make sure that we can create instances of the detail view controller from code.
To do so, open the storyboard file and select the detail view controller. Then, on the right-hand side of the screen, set the view controller's Storyboard ID to ContactDetailViewController, shown as follows:
This allows you to create an instance of this view controller from code. You'll learn exactly how in a second. First, we must adjust the ViewController's viewDidLoad() method by adding the following lines of code to it:
if traitCollection.forceTouchCapability == .available {
registerForPreviewing(with: self, sourceView: collectionView)
}
These lines check the current traitCollection to make sure that the environment supports 3D Touch, and if it does, the view controller registers itself as a previewing delegate for the collectionView. The final step is to implement UIViewControllerPreviewingDelegate for ViewController. We're going to implement conformance for this protocol though an extension. Implementing protocol conformance through extensions can be really helpful when your files start to get larger because they provide a nice way to group related methods together. At the bottom of ViewController.swift, add the following code:
extension ViewController: UIViewControllerPreviewingDelegate {
func previewingContext(_ previewingContext: UIViewControllerPreviewing, viewControllerForLocation location: CGPoint) -> UIViewController? {
guard let tappedIndexPath = collectionView.indexPathForItem(at: location)
else { return nil }
let contact = contacts[tappedIndexPath.row]
guard let viewController = storyboard?.instantiateViewController(withIdentifier: "ContactDetailViewController") as? ContactDetailViewController
else { return nil }
viewController.contact = contact
return viewController
}
func previewingContext(_ previewingContext: UIViewControllerPreviewing, commit viewControllerToCommit: UIViewController) {
navigationController?.show(viewControllerToCommit, sender: self)
}
}
This extension implements two methods: previewingContext(_:viewControllerForLocation) and previewingContext(_:commit). The first method is responsible for providing the previewed view controller. This is done by figuring out the tapped item in the collection view. Next, a view controller is obtained through the storyboard by using the identifier you configured earlier. Finally, the view controller is assigned a contact and returned for previewing.
The second method simply tells the navigation controller to present the view controller that was previewed. When you implement different preview view controllers for your detail pages, you can use the commit method to configure a real detail view controller instead of the preview.
If you run the project on a device that supports 3D Touch, you should be able to apply some force while tapping on a contact to see their preview.