Swift version: 5.6
Peek and pop are features of 3D Touch that let users press hard on something to show more information. It’s surprisingly easy to add when working with table view cells and collection view cells, and downright trivial if you use storyboards and segues.
First, the trivial case: if you’re using storyboards and segues, Xcode can do all the work for you. If you want to see how easy it is, create a new iOS app using the Master-Detail project template, then open Main.storyboard. Find the Show Detail segue that moves from the table view to the detail view, then check the box marked “Preview & Commit Segues” in the attributes inspector.
That’s it: iOS will automatically make peek and pop code for that segue – you can press hard on a table view cell to bring up the detail controller as a preview, then press harder to make it full screen. (Note: if you’re using the simulator this isn’t easy to do – try using a real device!)
If you’re not using segues you need to write some code yourself. First, make your view controller conform to UIViewControllerPreviewingDelegate
so that you’re able to respond to previewing requests correctly. Second, you need to tell the system we want to support previewing by calling registerForPreviewing()
in your viewDidLoad()
method:
registerForPreviewing(with: self, sourceView: yourTableView)
That tells iOS that the view controller is able to respond to previewing requests for your table view.
There are two methods you need to fill in for the UIViewControllerPreviewingDelegate
protocol: which view controller should be shown when the user presses at a certain location, and when the user presses harder how do you want to present it.
For many apps the code to create a previewing detail controller will be the same code to create a regular detail view controller, so it’s a good idea to create a method to instantiate a detail view controller and configure it as needed.
For example, you might have something like this:
func detailViewController(for index: Int) -> DetailViewController {
guard let vc = storyboard?.instantiateViewController(withIdentifier: "Detail") as? DetailViewController else {
fatalError("Couldn't load detail view controller")
}
vc.selectedItem = index
return vc
}
You would then have something like this inside your didSelectRowAt
method:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let vc = detailViewController(for: indexPath.row)
navigationController?.pushViewController(vc, animated: true)
}
Now for the important part: filling in the peek and pop methods.
You need to write a viewControllerForLocation
method, which gets called with whatever location on the screen the user touched. You can pass that straight to the table view to ask it what row is at that location, then tell the previewing context to use that row as its source rect – iOS will cause that row to zoom up while the others get blurred. Finally you can return the correct view controller, but if no cell was tapped you need to return nil
instead so that no 3D Touch effect happens.
Here’s how that looks in code:
func previewingContext(_ previewingContext: UIViewControllerPreviewing, viewControllerForLocation location: CGPoint) -> UIViewController? {
if let indexPath = yourTableView.indexPathForRow(at: location) {
previewingContext.sourceRect = yourTableView.rectForRow(at: indexPath)
return detailViewController(for: indexPath.row)
}
return nil
}
As for the pop effect, that’s just one line of code inside a commit
method. You’ll be given the view controller that’s currently being peeked, so your method just needs to decide how to present it. For example, if you’re using a navigation controller you probably want to call pushViewController()
here – iOS will automatically convert that into a pop zoom animation with bounce effect, but the end result will be a pushed view controller so you can go back like normal.
Here’s that in Swift:
func previewingContext(_ previewingContext: UIViewControllerPreviewing, commit viewControllerToCommit: UIViewController) {
navigationController?.pushViewController(viewControllerToCommit, animated: true)
}
That’s all the code – it’s not really that hard to do, and UIKit makes the result look great.
SPONSORED Let’s face it, SwiftUI previews are limited, slow, and painful. Judo takes a different approach to building visually—think Interface Builder for SwiftUI. Build your interface in a completely visual canvas, then drag and drop into your Xcode project and wire up button clicks to custom code. Download the Mac App and start your free trial today!
Sponsor Hacking with Swift and reach the world's largest Swift community!
Available from iOS 9.0
This is part of the Swift Knowledge Base, a free, searchable collection of solutions for common iOS questions.
Link copied to your pasteboard.