3D Touch Peek And Pop Tutorial

3D touch peek and pop tutorial for your Swift application

3D Touch peek and pop tutorial

Apple added a touch-sensitive layer to the screen of the brand new iPhone 6s (plus).  With the coming of this new screen, they’ve added some new UI interactions like application shortcuts and peek and pop.

In this 3D touch peek and pop tutorial I will learn you how to implement this new way of interacting with your content by building a photo gallery.  When you press hard on the screen you’ll see a preview of the image and if you press really hard the preview will pop into a detail view.

At the end of this tutorial I’ll show you how to add preview actions. This way you can interact with the content without going to the detail view.  You can do this by swiping up while you are previewing the content.

Quick links

Program file overview

For this application we’ll have the following classes:

  • PhotoCollectionViewController: this is the entry point for the application. We’ll show a list of photos in a UICollectionViewController.
  • PhotoCollectionViewCell: a subclass of UICollectionViewCell which will just hold an IBOutlet.
  • Photo: this struct will serve as our data object in which we’ll store some data like the name of the image file, the city where the picture was taken and a caption.
  • DetailViewController: when a user taps on a photo, we will transition to this view controller and show the user the photo and a caption.

Setup the datasource

Our UICollectionViewController needs to be fed with data. To do this we’ll create an array of Photo instances.  I’ll lazy load this datasource, so the array will only be created when the datasource is first accessed.

lazy var photos:[Photo] = {
    
    return [
        Photo(caption: "Lovely piece of art in Bordeaux", imageName: "bordeaux", city: "Bordeaux"),
        Photo(caption: "Cosy lake beach in France", imageName: "lake", city: "Bordeaux"),
        Photo(caption: "Harbour in France", imageName: "harbour", city: "Rouffiac"),
        Photo(caption: "Buda beach in Kortrijk", imageName: "buda", city: "Kortrijk")
    ]
    
}()

Check for 3D Touch support

If we want to integrate peek and pop, we need to be sure that the device has a 3D Touch screen. You can check this by asking the traitcollection object for the value of the forceTouchCapability property.

If the capability is available, we can register our view controller for peek and pop by calling registerForPreviewingWithDelegate:sourceView:I’ll do this check in viewDidLoad.

override func viewDidLoad() {
    
    super.viewDidLoad()
    
    if( traitCollection.forceTouchCapability == .Available){
        
        registerForPreviewingWithDelegate(self, sourceView: view)
        
    }
    
}

Implement the delegate

We just agreed to be the delegate for peeking and popping, so we need to implement the delegate methods.  These 2 methods are declared in UIViewControllerPreviewDelegate, before we can implement these methods we need to implement this protocol.  Just add the name of the protocol after the superclass.

class PhotoCollectionViewController: UICollectionViewController, UIViewControllerPreviewingDelegate {

...

}

There are 2 methods that need to be implemented, the first method will setup a view controller for peeking, the other method is for popping.

Setup the peeking

We need to implement the method previewContext:viewControllerForLocation:, this method will return a view controller object which in our case will be an instance of DetailViewController.

First we need to find out which photo the user touched, so we can find the correct model in our datasource.  We can ask the collectionview for the index path of the item in the collection view at a certain location. The location is passed as an argument by the delegate method.  If we can’t find an indexPath, we bail out.

With this indexPath, we can now check if there is a cell at that indexPath in the collectionView. If not we bail out.

func previewingContext(previewingContext: UIViewControllerPreviewing, viewControllerForLocation location: CGPoint) -> UIViewController? {
        
        guard let indexPath = collectionView?.indexPathForItemAtPoint(location) else { return nil }
        
        guard let cell = collectionView?.cellForItemAtIndexPath(indexPath) else { return nil }

...

}

The next step is to create an instance of the DetailViewController class.  I’ve added a Storyboard ID to the DetailViewController scene in my storyboard. If this view controller fails to be created, we bail out.

guard let detailVC = storyboard?.instantiateViewControllerWithIdentifier("DetailViewController") as? DetailViewController else { return nil }

If all the above steps worked, we can fetch the data object from the datasource and pass this object to the DetailViewController so it can set up it’s imageview and label.

let photo = photos[indexPath.row]
detailVC.photo = photo

If you check the code in DetailViewController, you’ll see that in the viewDidLoad method there is a check to see if the photo property is available. If so, the image, caption and title are set.

We only need to do 2 more step, one is to set the preferredContentSize on the DetailViewController instance.

detailVC.preferredContentSize = CGSize(width: 0.0, height: 300)

And the last step is to set the sourceRect property. This property accepts a CGRect and will blur everything outside this rect, and keep the content inside the CGRect sharp.  The rect is the same frame as the cell of our collectionview.

previewingContext.sourceRect = cell.frame

Now we just have to return the instance of DetailViewController.  So this is the final method.

func previewingContext(previewingContext: UIViewControllerPreviewing, viewControllerForLocation location: CGPoint) -> UIViewController? {
    
    guard let indexPath = collectionView?.indexPathForItemAtPoint(location) else { return nil }
    
    guard let cell = collectionView?.cellForItemAtIndexPath(indexPath) else { return nil }
    
    guard let detailVC = storyboard?.instantiateViewControllerWithIdentifier("DetailViewController") as? DetailViewController else { return nil }
    
    let photo = photos[indexPath.row]
    detailVC.photo = photo
    
    detailVC.preferredContentSize = CGSize(width: 0.0, height: 300)
    
    previewingContext.sourceRect = cell.frame
    
    return detailVC
}

Setup the popping

Popping is easy, we’ll just use the same view controller we just created.  Just implement the delegate method and call the showViewController:sender: method.

func previewingContext(previewingContext: UIViewControllerPreviewing, commitViewController viewControllerToCommit: UIViewController) {
    
    showViewController(viewControllerToCommit, sender: self)
    
}

And you’re done! Run the application and press on a picture, it should popup and if you press harder you’ll pop inside the DetailViewController.

3D Touch Peek
3D Touch Peek

Adding preview actions

If you want to allow the user to swipe up when they are in the peek phase and do an action, you can implement something called preview actions.

3D Touch preview actions
3D Touch preview actions

The implementation is really simple. In the DetailViewController you need to implement the previewActionItems method.  This method returns an array of objects that implement the UIPreviewActionItem protocol.

A UIPreviewAction (which implements this protocol) has 3 arguments you have to provide

  • title: the title for the button
  • style: the style for the button
  • handler: a closure which gets executed when the user taps the action

For this application I’ll add 2 dummy buttons, one to like the photo and one to delete the photo.

Add the following code to your DetailViewController class.

override func previewActionItems() -> [UIPreviewActionItem] {
    
    let likeAction = UIPreviewAction(title: "Like", style: .Default) { (action, viewController) -> Void in
        print("You liked the photo")
    }
    
    let deleteAction = UIPreviewAction(title: "Delete", style: .Destructive) { (action, viewController) -> Void in
        print("You deleted the photo")
    }
    
    return [likeAction, deleteAction]
    
}

Wrapping up

That’s it! You succesfully implemented 3D touch peek and pop functionality.
I hope you liked this tutorial, if you have any questions … just leave a comment.

Sourcecode

The code for this project is available on GitHub.

 

%d bloggers like this: