Wrapper for 3D touch preview implementation using Swift 3.0

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
sourcedirectory located under root inside your project.Touch3DPreviewViewController.swiftTouch3DWrapperProtocol.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 ofTouch3DPreviewViewControllerobject -
func locationFrom(point: CGPoint, in view: UIScrollView) -> CGPointTakes the touch point as an input and returns the point position relative to
scrollViewortableView.This method comes handy when user starts 3D touch on the
tableView/collectionViewcell and we want to get relative position of touch point relative to thescrollViewin which cell is located.
The following method gets called when user begins 3D touch on
scrollViewcell.
public func previewingContext(_ previewingContext: UIViewControllerPreviewing, viewControllerForLocation location: CGPoint) -> UIViewController?
-
Implementing
UIViewControllerPreviewingDelegatemethodsAfter
UIViewControllerconforms toUIViewControllerPreviewingDelegateprotocol, 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
tableVieworcollectionView. Assuming, client has conformed toTouch3DRecognizerProtocoland implementedfunc locationFrom(point: CGPoint, in view: UIScrollView) -> CGPointmethod, we can get relative touch position by callinglocationFrommethod passing touch point andscrollViewsubclass.Once we get relative touch point, next few things are left to client to calculate to pass to
Touch3DPreviewViewControllerinitializer. -
informationObjectThis is an object which gets passed from current
viewControllertopreviewViewControllerand then to final destinationviewController. In order to simplify implementation, this could be object ofAnytype -
touchPreviewActionItemsThis is the collection of preview items which accompany the options presented to user on
previewViewController. User can pass them by calling implementation offunc actionItems() -> [UIPreviewActionItem]which is the part ofTouch3DRecognizerProtocolprotocol -
thumbnailImageThis is a thumbnail image which is passed to
previewViewControllerand is presented as a part of preview of selected choice
-
-
After we calculate 3 items mentioned in previous point, we can initialize our
Touch3DPreviewViewControllerand 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
UIViewControllerPreviewingDelegateprotocol. This method is called when user has applied enough force to make transition from currentviewControllerto directly destinationviewControllerdismissingpreviewViewController. In this method, you can grab theinformationObjectpassed toTouch3DPreviewViewControlleras 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

