An iOS 10 3D Touch Peek and Pop Tutorial

From Techotopia
Revision as of 04:33, 10 November 2016 by Neil (Talk | contribs) (Assigning the Detail Controller Storyboard ID)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

PreviousTable of ContentsNext
An iOS 10 3D Touch Quick Actions TutorialImplementing TouchID Authentication in iOS 10 Apps


Learn SwiftUI and take your iOS Development to the Next Level
SwiftUI Essentials – iOS 16 Edition book is now available in Print ($39.99) and eBook ($29.99) editions. Learn more...

Buy Print Preview Book


As outlined in the chapter entitled An Overview of iOS 10 Multitouch, Taps and Gestures, the 3D Touch system supports a feature known as “peek and pop”. When implemented, touching an item with increased force on a 3D Touch enabled device displays a “peek” preview view of another view controller while a further increase in force “pops” to that view controller (also referred to as the commit view). At the time that the peek preview is displayed, performing an upward sliding motion displays a set of preview action buttons if any have been configured.

This chapter will work through the steps that are required to integrate 3D Touch peek and pop capabilities into an iOS application including the addition of preview action items within the peek view.


Contents


About the Example Project

For the purposes of this tutorial, and for the avoidance of repetition of features already covered in preceding chapters, the 3D Touch peek and pop behavior implemented in this chapter will be added to the TableViewStory project created in the chapters entitled Using Xcode 8 Storyboards to Build Dynamic TableViews and Implementing iOS 10 TableView Navigation using Storyboards in Xcode 8. If you have already completed these chapters, launch Xcode and locate and open this project. Alternatively, the completed table view example is included as part of the sample code archive for this book at the following web page:

http://www.ebookfrenzy.com/web/ios10

Adding the UIViewControllerPreviewDelegate

The first step in supporting peek and pop behavior within a view controller of an iOS app is to implement the UIViewControllerPreviewDelegate protocol within that view controller class. In this example, the peek and pop behavior is going to be added to the table view controller scene which is represented by the AttractionTableViewController class. Rather than implement this directly within the class declaration contained in the AttractionTableViewController.swift file, however, we will instead extend this class to include the implementation of the protocol.

With the TableViewStory project loaded into Xcode, select the File -> New -> File… menu option and, in the resulting panel, choose the Swift File template option in the main panel before clicking on the Next button:


Xcode 7 add extension file.png

Figure 59-1

Learn SwiftUI and take your iOS Development to the Next Level
SwiftUI Essentials – iOS 16 Edition book is now available in Print ($39.99) and eBook ($29.99) editions. Learn more...

Buy Print Preview Book

On the subsequent screen, name the new source file as follows before creating the file:

AttractionTableViewController+UIViewControllerPreviewing.swift

Once created, select the new file and modify it to import UIKit instead of the Foundation framework and to declare the file as an extension of the AttractionTableViewController class which implements the UIViewControllerPreviewingDelegate protocol:

import UIKit

extension AttractionTableViewController: 
	UIViewControllerPreviewingDelegate {
}

Implementing the Peek Delegate Method

The first method to be implemented in the extension is the previewingContext delegate method which will be called upon to provide the view controller that will display the peek view to the user. For this example, this method will need to identify the row within the table view on which the user is currently pressing prior to instantiating, configuring and returning an instance of the AttractionDetailViewController class. Remaining within the extension source file, therefore, implement this method as follows:

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

    guard let indexPath = tableView.indexPathForRow(at: location),
        let cell = tableView.cellForRow(at: indexPath) else {
                return nil }

    guard let detailViewController =
        storyboard?.instantiateViewController(
            withIdentifier: "AttractionDetailViewController") as?
                        AttractionDetailViewController else { return nil }

    detailViewController.webSite = webAddresses[indexPath.row]
    detailViewController.preferredContentSize =
        CGSize(width: 0.0, height: 600)

    previewingContext.sourceRect = cell.frame

    return detailViewController
}

The code begins by identifying the currently selected row within the table view and, in the event that no row is selected, returns a nil value:

Learn SwiftUI and take your iOS Development to the Next Level
SwiftUI Essentials – iOS 16 Edition book is now available in Print ($39.99) and eBook ($29.99) editions. Learn more...

Buy Print Preview Book

guard let indexPath = tableView.indexPathForRow(at: location),
      let cell = tableView.cellForRow(at: indexPath) else {
             return nil }

If a table row is currently selected, the code then attempts to create an instance of the AttractionDetailViewController scene from the storyboard file, once again returning in the event that the instance could not be created:

guard let detailViewController =
    storyboard?.instantiateViewController(
        withIdentifier: "AttractionDetailViewController") as?
                   AttractionDetailViewController else { return nil }

Once an instance of the detail view controller has been created it is configured with the web page URL to be loaded into the web view using the currently selected row as the index into the array of web site addresses:

detailViewController.webSite = webAddresses[indexPath.row]

Next, the size of the preview view is configured to have a height of 600 points. The width is set to zero to allow the system to choose an appropriate width for the screen size and device orientation:

detailViewController.webSite = webAddresses[indexPath.row]
detailViewController.preferredContentSize = 
			CGSize(width: 0.0, height: 600)

The sourceRect property of the previewing context object passed to the method is set to the cell frame of the currently selected table row. This will cause the cell to remain in focus while the rest of the table view is blurred. Finally, the detail view controller is returned ready to be displayed to the user:

previewingContext.sourceRect = cell.frame
return detailViewController

Assigning the Detail Controller Storyboard ID

The code added above to create an instance of the detail view controller does so by referencing the storyboard ID assigned to the AttractionDetailViewController scene in the storyboard file. As of yet, however, the referenced storyboard ID has not been assigned to this view controller. This needs to be addressed by selecting the Main.storyboard file in the Project Navigator panel and selecting the AttractionDetailViewController scene so that it highlights in blue. Once selected, display the Identity Inspector panel and enter AttractionDetailViewController into the Storyboard ID field as shown in Figure 59-2:


Ios 9 3d touch peak storyboard id.png

Figure 59-2

Implementing the Pop Delegate Method

With the peek view controller delegate method written, the method to pop to the full detail view controller now needs to be added to the extension as follows:

func previewingContext(_ previewingContext: UIViewControllerPreviewing, commit viewControllerToCommit: UIViewController) {

    show(viewControllerToCommit, sender: self)
}

Learn SwiftUI and take your iOS Development to the Next Level
SwiftUI Essentials – iOS 16 Edition book is now available in Print ($39.99) and eBook ($29.99) editions. Learn more...

Buy Print Preview Book

When this method is called it is passed a reference to the detail view controller that was created in the peek delegate method. All that this method needs to do, therefore, is call the current view controller’s show(viewControllerToCommit:) method passing along the detail view controller reference.

Registering the Previewing Delegate

If the app were to be run on a 3D Touch capable iOS device at this point the peek and pop features would not work. The reason for this is that although the AttractionTableViewController class has now been declared as implementing the previewing protocol, the view controller has not been registered as the previewing delegate. Edit the AttractionTableViewController.swift file, locate the viewDidLoad method and add the code to check for 3D Touch support and to register the delegate:

override func viewDidLoad() {

    super.viewDidLoad()

    attractionNames = ["Buckingham Palace",
                       "The Eiffel Tower",
                       "The Grand Canyon",
                       "Windsor Castle",
                       "Empire State Building"]

    webAddresses = ["https://en.wikipedia.org/wiki/Buckingham_Palace",
                  "https://en.wikipedia.org/wiki/Eiffel_Tower",
                  "https://en.wikipedia.org/wiki/Grand_Canyon",
                  "https://en.wikipedia.org/wiki/Windsor_Castle",
                  "https://en.wikipedia.org/wiki/Empire_State_Building"]

    attractionImages = ["buckingham_palace.jpg",
                        "eiffel_tower.jpg",
                        "grand_canyon.jpg",
                        "windsor_castle.jpg",
                        "empire_state_building.jpg"]

    tableView.estimatedRowHeight = 50

    if traitCollection.forceTouchCapability == .available {
        registerForPreviewing(with: self, sourceView: view)
    } else {
        print("3D Touch Not Available")
    }
}

Testing the Peek and Pop Behavior

Compile and run the app on an iOS device with 3D Touch support (3D Touch is not currently supported in the Simulator environment). Once running, press on one of the table rows. This should cause the selected table row to remain in focus while the remainder of the screen moves out of focus:


Ios 9 3d touch peek focus small.png

Figure 59-3

Learn SwiftUI and take your iOS Development to the Next Level
SwiftUI Essentials – iOS 16 Edition book is now available in Print ($39.99) and eBook ($29.99) editions. Learn more...

Buy Print Preview Book

Gradually increase the force applied to the touch until the peek view appears on the screen as illustrated in Figure 59-4:


Ios 9 3d touch peek view.png

Figure 59-4


Finally, apply greater force until the app pops to the full detail view controller screen:


Ios 9 3d touch pop view.png

Figure 59-5

Adding Peek Quick Actions

Peek quick actions (also referred to as preview actions) take the form of an optional series of buttons that appear when the user makes an upward sliding motion on the screen while the peek preview is visible. The availability of quick actions is indicated by an upward pointing arrow at the top of the peek view as highlighted in Figure 59-6:


Ios 9 3d touch action arrow.png

Figure 59-6


Quick actions are configured from within the destination view controller and are created either individually as UIPreviewActionItem instances, or in groups of items that appear in sub-menus using the UIPreviewActionGroup class. Once an instance of the destination view controller has been created in preparation for appearing in the peek preview, the previewActions method of that view controller will be called if it has been implemented. When called, the method will need to return an array of UIPreviewActionItem and UIPreviewActionGroup items that are to be displayed as action options to the user.

A preview action item consists of a title, a style and a handler to be called when the user selects the action in the preview. iOS 10 supports three different styles in the form of selected, destructive and default. Default actions appear in blue text, destructive in red and selected in blue text with a check mark.

In this tutorial two individual preview actions will be added along with an action group containing two additional action items. In the case of the two individual actions, these will be configured to demonstrate the appearance of both the selected and destructive styles. The handler code will simply output the selection made by the user to the Xcode console. The previewActionItems method containing the code to achieve these goals needs to be added to the AttractionDetailViewController.swift file as follows:

Learn SwiftUI and take your iOS Development to the Next Level
SwiftUI Essentials – iOS 16 Edition book is now available in Print ($39.99) and eBook ($29.99) editions. Learn more...

Buy Print Preview Book

override var previewActionItems: [UIPreviewActionItem] {

    let action1 = UIPreviewAction(title: "Action One",
                        style: .destructive,
                        handler: { previewAction, viewController in
                                print("Action One Selected")
                        })

    let action2 = UIPreviewAction(title: "Action Two",
                        style: .selected,
                        handler: { previewAction, viewController in
                                print("Action Two Selected")
                        })

    let groupAction1 = UIPreviewAction(title: "Group Action One",
                          style: .default,
                          handler: { previewAction, viewController in
                                print("Group Action One Selected")
                          })

    let groupAction2 = UIPreviewAction(title: "Group Action Two",
                          style: .default,
                          handler: { previewAction, viewController in
                                print("Group Action Two Selected")
                          })

    let groupActions = UIPreviewActionGroup(title: "My Action Group...",
                                style: .default,
                                actions: [groupAction1, groupAction2])

    return [action1, action2, groupActions]
}

Compile, run and interact with the app once again, this time making an upward sliding motion when the preview peek view appears (Figure 59 7). Note that the actions are now available for selection and that tapping the My Action Group… item displays the two group action items. Selecting any of the actions should result in the corresponding output appearing in the Xcode console.


Ios 9 3d touch peek actions.png

Figure 59-7


Summary

In this chapter we have explored the steps necessary to implement 3D Touch peek and pop within an iOS 10 app. Basic 3D Touch peek and pop behavior in iOS 10 involves implementing the UIViewControllerPreviewingDelegate methods within the view controller on which peek and pop is to be available and registering that class as the preview delegate.

The chapter has also covered the addition of preview actions and action groups to the peek preview view using the UIPreviewActionItem and UIPreviewActionGroup classes.


Learn SwiftUI and take your iOS Development to the Next Level
SwiftUI Essentials – iOS 16 Edition book is now available in Print ($39.99) and eBook ($29.99) editions. Learn more...

Buy Print Preview Book



PreviousTable of ContentsNext
An iOS 10 3D Touch Quick Actions TutorialImplementing TouchID Authentication in iOS 10 Apps