3D Touch Introduction

Original author: Maxime Defauw
  • Transfer
With the release of the iPhone 6s and iPhone 6s Plus, Apple has introduced a completely new way to interact with our phones: a touch gesture. As you know, this feature is already available on the Apple Watch and MacBook and MacBook Pro called Force Touch. This - literally - has added a new concept to the user interface.

If you're wondering why Force Touch has been renamed 3D Touch for iPhone, then you're not the only one. Shortly after Craig Federighi , who was also clearly puzzled by the name, introduced this new opportunity, a lot of speculation arose. What did not suit the name Force Touch? Too many Star Wars jokes?

But there is a difference! Obviously, Force Touch can only recognize a strong press, while the function3D Touch is more sensitive and can highlight several levels of taps based on how much you press.

3D Touch Introduction

Although the changes may seem insignificant, they allow developers to make measurements on the iPhone more accurate. Take this Gravity app , for example, that turns your iPhone into a digital scale using Force Touch. Although it was rejected by Apple for unclear reasons, the idea is great. Thus, to show you how 3D Touch works, let's try to make a similar application!

let's start


To get started, download this project template that I created. In fact, this is just an empty creation application based on the Single View iPhone application template . I created a project hosted (UILabels & UIImage) and hooked IBOutlets into ViewController.swift .

image

The design of our application is quite simple: we have one controller with 2 tags: one is the title and the second is the label, which shows the percentage of pressure on the iPhone screen.

Let's get started! On iPhone 6s and 6s Plus, UITouch objects have two new properties like CGFloat, called force and maximumP possibleForce. Force represents how strong the touch is, where 1.0 means average touch. MaximumPossibleForce shows the maximum strength to touch.

Whenever the user clicks on the sensor, the touchesBegan method is called, and then touchesMoved, if the user moves his finger on the screen, then touchedCancelled or TouchesEnded is called, depending on the situation. For our purposes, the touchesMoved methodis the only way we need. TouchesMoved has two parameters: touches (touch) and event (event). Touch is the set (unordered set of various objects) of UITouch objects. There must be exactly one UITouch object to touch, but we may not be careful enough, so it is strongly recommended that you check whether touches.first (the first UITouch object of the touch set) is nil with an additional binding. Paste the following method into ViewController.swift :
override func touchesMoved(touches: Set, withEvent event: UIEvent?) {
    if let touch = touches.first {
        if #available(iOS 9.0, *) {
            if traitCollection.forceTouchCapability == UIForceTouchCapability.Available {
                // 3D Touch capable
            }
        }
    }
}

Using the if statement, we check if the device supports 3D Touch. This part is optional if you are doing this project for fun. However, if you intend to publish the application on the App Store, it is a prerequisite to perform a check, since older devices such as the iPhone 6 do not support 3D Touch.

Please note that I also checked if the device runs on iOS 9.0 or later. I do this with the new #available syntax introduced in Swift 2.0. If you want to learn more about the new features and capabilities in Swift 2.0, I recommend that you read this article. Again, this check is optional if your target platform for application is 9.0 or higher.

To get the percentage of pressing force, simply divide the contact force by the maximum force (i.e. touch.maximumPossibleForce), which is the maximum possible pressing force. Then update the label text. You can update the method as follows:
override func touchesMoved(touches: Set, withEvent event: UIEvent?) {
    if let touch = touches.first {
        if #available(iOS 9.0, *) {
            if traitCollection.forceTouchCapability == UIForceTouchCapability.Available {
                // 3D Touch capable
                let force = touch.force/touch.maximumPossibleForce
                forceLabel.text = "\(force)% force"
            }
        }
    }
}

If you run the application on the iPhone 6s / 6s Plus, it should show the percentage of pressure when you click on the screen. However, since we are making the scale, you might want to add the number of grams that you weigh on your iPhone. According to Ryan MacLead , the sensor will weigh a maximum weight of ~ 385g. Thus, the maximum PossibleForce corresponds to 385 grams (approximately 3.8 Newton). By simple calculations, you can convert% force to grams. All we need to do is multiply the percentage of pressing force by 385. For objects weighing 385 grams or more, we simply change the label to “385+ grams”.

Now update the method using the following code snippet:
override func touchesMoved(touches: Set, withEvent event: UIEvent?) {        
    if let touch = touches.first {
        if #available(iOS 9.0, *) {
            if traitCollection.forceTouchCapability == UIForceTouchCapability.Available {
                if touch.force >= touch.maximumPossibleForce {
                    forceLabel.text = "385+ grams"
                } else {
                    let force = touch.force/touch.maximumPossibleForce
                    let grams = force * 385
                    let roundGrams = Int(grams)
                    forceLabel.text = "\(roundGrams) grams"
                }
            }
        }
    }
}

Cool! You have made your Digital Scale application.

image

At the moment, the application does not lose weight to zero after you remove the item or stop touching your screen. You can implement the touchesEnded method to reset the label.
override func touchesEnded(touches: Set, withEvent event: UIEvent?) {
    forceLabel.text = "0 gram"
}


Quick actions on the main screen


Another great use of 3D Touch is the quick action on the home screen. Quick Actions give users a menu to navigate directly to your application directory. Just press the program icon hard and you will see a menu. With the advent of 3D Touch technology, apps like Twitter and Instagram and some other apps have shown how to use this new feature.

image

Let's add Quick Actions for our application that we just made, with which we will open the application with a blue background instead of white. To add Quick Actions, open the info.plist fileOf your project (Click on the Scale workspace in the project navigator, select Scale target and go to the Info tab). In the file, add the 'UIApplicationShortcutItems' type, select an array (Array). Each element of the array is a dictionary containing the properties of one quick action:
  • UIApplicationShortcutItemType ( required ): A string that identifies the quick action. Note that this string must be unique, specific to the application. A good idea is to add a prefix using your identifier or some other tricks to make a unique string.
  • UIApplicationShortcutItemTitle ( required ): string, represents the title for the Quick Action that is shown to the user. An example would be “Show last picture taken”.
  • UIApplicationShortcutItemSubtitle ( optional ): a string for the name of your Quick Action. An example would be “Last picture taken yesterday”. If you want to add an icon for Quick Action, you have two options: an Apple system icon or a custom icon.
  • UIApplicationShortcutItemIconType ( optional ): A string indicating which system icon will be displayed next to Quick Action.
  • UIApplicationShortcutItemIconFile ( optional ): A string indicating which custom icon will be displayed next to Quick Action.
  • UIApplicationShortcutItemUserInfo ( optional ): A dictionary containing some additional information that you want to perform with Quick Action.

In the array, we define four elements for setting the Quick Action “OpenBlue”. Now info.plist should look like this:

image

Note that I used ' $ (PRODUCT_BUNDLE_IDENTIFIER) ' instead of ' com.appcoda.Scale ' or any other identifier that you use. This is for security reasons: if, for any reason, I change the Bundle ID to 'General', the whole project will be affected and the identifier will be changed everywhere. Otherwise, I will have to change it everywhere manually. In your info.plist file, you can see that the Bundle Identifier key uses the same approach: ' $ (PRODUCT_BUNDLE_IDENTIFIER) ' describes the path to the identifier of your project.

The last thing left to do is implement the Quick action when the user launches it. The main work is handled in AppDelegate.swift in the performActionForShortcutItem method . When Quick Action is activated, the method is called. Therefore, you must implement this method to handle quick actions:
func application(application: UIApplication, performActionForShortcutItem shortcutItem: UIApplicationShortcutItem, completionHandler: (Bool) -> Void) {
    // Handle quick actions
    completionHandler(handleQuickAction(shortcutItem))
}

As expected, call the handler and pass it a null value, depending on the success / failure of the fast action. Here we create a separate handleQuickAction function to handle the action. A great way to represent several cases of Quick Action is to use enumerations using ' UIApplicationShortcutItemType ' as the raw value. Declare an enumeration and implement the handleQuickAction method , as shown below, to set the background color to blue when the application is launched by quick action:
enum Shortcut: String {
    case openBlue = "OpenBlue"
}
func handleQuickAction(shortcutItem: UIApplicationShortcutItem) -> Bool {
    var quickActionHandled = false
    let type = shortcutItem.type.componentsSeparatedByString(".").last!
    if let shortcutType = Shortcut.init(rawValue: type) {
        switch shortcutType {
        case .openBlue:
            self.window?.backgroundColor = UIColor(red: 151.0/255.0, green: 187.0/255.0, blue: 255.0/255.0, alpha: 1.0)
            quickActionHandled = true
        }
    }
    return quickActionHandled
}

It is pretty simple. If you now launch the application and run it through quick actions, the background will turn blue.

image

What should not be forgotten?


There is one thing to consider. In terms of startup sequence, there is a difference between an application that starts and an application that resumes using Quick Action. Since you know when the application starts, the willFinishLaunchingWithOptions methods are called, followed by the anddidFinishLaunchingWithOptions method . But when the application is resumed by Quick Actions, it only provokes the execution of the performActionForShortcutItem method .

image

If you look at the didFinishLaunchingWithOptions method, you will see that we have a line of code to set the background color to white. It is used when the application starts normally through the application icon.
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions:
    [NSObject: AnyObject]?) -> Bool {
    // Override point for customization after application launch.
    self.window?.backgroundColor = UIColor.whiteColor()
    return true
}

This is where the problem comes from: when you start the application using a quick action, willFinish , didFinish are called , and then performActionForShortcutItem . Therefore, first the white color of the background is set, and then it changes to blue. Obviously, you do not want to set the background color to white when the user launches the application with quick actions.

To solve this problem, we need to check in the didFinishLaunchingWithOptions method :
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions:
    [NSObject: AnyObject]?) -> Bool {
    print("didFinishLaunchingWithOptions called")
    var isLaunchedFromQuickAction = false
    // Check if it's launched from Quick Action
    if let shortcutItem = launchOptions?[UIApplicationLaunchOptionsShortcutItemKey] as? UIApplicationShortcutItem {
        isLaunchedFromQuickAction = true
        // Handle the sortcutItem
        handleQuickAction(shortcutItem)
    } else {
        self.window?.backgroundColor = UIColor.whiteColor()
    }
    // Return false if the app was launched from a shortcut, so performAction... will not be called.
    return !isLaunchedFromQuickAction
}

To determine if an application is running through Quick Action, you can check the UIApplicationLaunchOptionsShortcutItemKey launch softkey . The UIApplicationShortcutItem object is available as the value of the launch softkey. If the application is launched using Quick Action, we simply call handleQuickAction to change the background to blue.

Since we already handled the quick action in didFinishLaunchingWithOptions , we do not want to call performActionForShortcutItem to execute handleQuickAction again. Therefore, we return false, telling the system not to call the performActionForShortcutItem method.

That's all! You can now test the application. Quick Action should work fine.

Total


3D Touch is a great way to add convenient and enjoyable features to your application. However, you should be aware that not all devices support 3D Touch, although this may change in the future.

After reading this article, you can add Quick Actions to your iOS application and determine the strength of the touch.

For comparison, you can download the full Xcode project . Be sure to share your thoughts in the comments about this guide and 3D Touch.

Also popular now: