Wrapper for 3D touch preview implementation using Swift 3.0

3d_touch_demo_implementation

It's been a while since I started working on 3D touch implementation on iOS 6s Plus devices. Luckily, even through it's a hardware feature iOS simulators still supports simulation of 3D touch of supported devices. I wrote a sample project demonstrating the use of 3D touch along with blog post accompanying this project.

However, I have been thinking about this - Can we hide implementation details to end user so that they don't have to know all about 3D touch code? The product of my thinking was a project for providing wrapper for implementing 3D touch in iOS apps. Although I feel like it is doing pretty much what it is supposed to do given the fairly understandable documentation, I have a strong feeling that it can still do better. However, for this blog post I am going to assume the version 0.0.1 which as been recently released on the Github.

Please note that in order to successfully run this project, you have to run it in the 3D touch enabled device or simulator. I hope documentation below is clear enough to understand. If there is any ambiguity or unclear part, please do refer to the sample project demo inside main library or you can even contact me directly with any question you might have.

  • Adding library to the client app

    In order to start using 3D touch wrapper, all you can to do is that drag and drop following two files from source directory located under root inside your project.

    • Touch3DPreviewViewController.swift
    • Touch3DWrapperProtocol.swift
  • Register for 3D touch

To use 3D touch, a UIViewController subclass must register for 3D touch on a given view. In order to do so, you must call registerFor3DTouch method from UIViewController's lifestyle method named viewDidLoad. Also, make sure the viewController where you are planning to present 3D preview from conforms to 3D previewing protocol UIViewControllerPreviewingDelegate before making call to registerFor3DTouch.

  • Conformance to Touch3DRecognizerProtocol

Next step is to make UIViewController subclass to conform to protocol Touch3DRecognizerProtocol and implement following two protocol methods.

  • func actionItems() -> [UIPreviewActionItem]

    Returns the action items associated with 3D touch previewing viewController. It could be any number of items where you can declare any arbitrary items and pass them during initialization of Touch3DPreviewViewController object

  • func locationFrom(point: CGPoint, in view: UIScrollView) -> CGPoint

    Takes the touch point as an input and returns the point position relative to scrollView or tableView.

    This method comes handy when user starts 3D touch on the tableView/collectionView cell and we want to get relative position of touch point relative to the scrollView in which cell is located.

The following method gets called when user begins 3D touch on scrollView cell.

public func previewingContext(_ previewingContext: UIViewControllerPreviewing, viewControllerForLocation location: CGPoint) -> UIViewController?
  • Implementing UIViewControllerPreviewingDelegate methods

    After UIViewController conforms to UIViewControllerPreviewingDelegate protocol, it requires to implement following two delegate methods.

    • public func previewingContext(_ previewingContext: UIViewControllerPreviewing, viewControllerForLocation location: CGPoint) -> UIViewController?

      This is a delegate method which gets called after user begins 3D touch on any cell of either tableView or collectionView. Assuming, client has conformed to Touch3DRecognizerProtocol and implemented func locationFrom(point: CGPoint, in view: UIScrollView) -> CGPoint method, we can get relative touch position by calling locationFrom method passing touch point and scrollView subclass.

      Once we get relative touch point, next few things are left to client to calculate to pass to Touch3DPreviewViewController initializer.

    • informationObject

      This is an object which gets passed from current viewController to previewViewController and then to final destination viewController. In order to simplify implementation, this could be object of Any type

    • touchPreviewActionItems

      This is the collection of preview items which accompany the options presented to user on previewViewController. User can pass them by calling implementation of func actionItems() -> [UIPreviewActionItem] which is the part of Touch3DRecognizerProtocol protocol

    • thumbnailImage

      This is a thumbnail image which is passed to previewViewController and is presented as a part of preview of selected choice

  • After we calculate 3 items mentioned in previous point, we can initialize our Touch3DPreviewViewController and return it


let previewViewController = Touch3DPreviewViewController(informationObject: selectedPlayerNameString, touchPreviewActionItems: self.actionItems(), thumbnailImage: thumbnailImage)
return previewViewController

Also, make sure to provide following check in the beginning of this method in order to prevent Touch3DPreviewViewController from being displayed multiple times


if let _ = self.presentedViewController as? Touch3DPreviewViewController {
    return nil
}

If any on the desired condition is not satisfied, you can simply return nil from the method.

  • public func previewingContext(_ previewingContext: UIViewControllerPreviewing, commit viewControllerToCommit: UIViewController)

    This is the second and final mandatory method client app should implement as a part of UIViewControllerPreviewingDelegate protocol. This method is called when user has applied enough force to make transition from current viewController to directly destination viewController dismissing previewViewController. In this method, you can grab the informationObject passed to Touch3DPreviewViewController as mentioned in previous point.

Once we get hold of informationObject, it can be cast into any custom type and can be further passed to destination viewController in the form of property as follows,


public func previewingContext(_ previewingContext: UIViewControllerPreviewing, commit viewControllerToCommit: UIViewController) {
	if let previewVC = viewControllerToCommit as? Touch3DPreviewViewController {
	    if let imageName = previewVC.informationObject as? [implementation-specific-inbuilt-or-custom-object-type] {
	        // Beginning of sample code - Please replace it with your own implementation 
	        if let imageViewController = self.storyboard?.instantiateViewController(withIdentifier: "image") as? ImageViewController {
	            imageViewController.imageName = imageName
	            let navController = UINavigationController(rootViewController: imageViewController)
	            self.present(navController, animated: true, completion: nil)

	        }
	        // End of Sample code
	    }
	}
}

Where ImageViewController is your dummy destination viewController which you want to make transition to.

Please refer to demo project added to library. I am working on getting rid of code which makes client to implement certain parts of preview implementation. I did it to offer extra flexibility and avoid pressing too much implementation constraints on the client side. If you have any suggestions on further improvement, please let me know