UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

How to measure touch strength using 3D Touch

3D Touch is a new technology that was first trialled in Apple Watch as Force Touch, but introduced fully inside the iPhone 6s. In iOS it's responsible for multiple interesting technologies: peek and pop (to preview and jump into view controllers), application shortcuts (menus on the home screen for common actions) and also pressure-sensitive taps for UITouch.

All three of these are surprisingly simple to do, but in this project we're going to use only the last one, and I think you'll be impressed by how easy it is. This project is about producing a hoax, and we're going to make it so that if you press hard on any card it will automatically become a star. This allows you to be able to "guess" correctly even without an Apple Watch around, because any card is the right answer as long as you press correctly.

To accomplish this, we're going to use two new properties of UITouch: force and maximumPossibleForce. The first tells us how strongly the user is pressing for the current touch, and the second tells us the maximum recognizable strength for the current touch. For our purposes, we just need to make sure the two match: if the user is pressing as hard as the screen can recognize, we'll enable our cheat.

The cheat itself is really simple, because we just need to change the image on the front of the card and set its isCorrect property to be true.

There is one small problem here, but it's trivial to fix: devices older than the iPhone 6s devices don't support 3D Touch, and even 3D Touch devices can have the feature disabled on user request. So, we need to add a simple check to ensure 3D Touch is available and enabled on our current device.

That's how it all needs to work in theory, but now for the implementation. To keep things as straightforward as possible, we're going to add all this work to touchesMoved() in ViewController.swift, which will get called every time the user's finger moves on the screen. Inside this method, we'll find where the user's touch was, then loop through all the cards to find which one (if any) they are over. Then, if they are over a card and are pressing hard enough, we'll enable the cheat.

Add this method to ViewController.swift now:

override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
    super.touchesMoved(touches, with: event)

    guard let touch = touches.first else { return }
    let location = touch.location(in: cardContainer)

    for card in allCards {
        if card.view.frame.contains(location) {
            if view.traitCollection.forceTouchCapability == .available {
                if touch.force == touch.maximumPossibleForce {
                    card.front.image = UIImage(named: "cardStar")
                    card.isCorrect = true

That contains three pieces of code that we haven't looked at before. The first two are tiny but important, so I want to cover them briefly before moving on. The first is location(in:), which is the UIKit version of the location(in:) method we've used in SpriteKit several times. The second is the contains() method of CGRect, which returns true if a point is inside the rectangle.

I told you it was tiny, but it's definitely important: our point is the location of the current touch, and our rectangle is the frame of each card. So, this method returns true if the user's finger is over a particular card.

The third piece of new code is the check whether 3D Touch is available, although as you can see the check is actually for "force touch" being available – presumably because Apple's marketing department got involved after development had completed! This is done by reading the current trait collection for the view and checking whether its forceTouchCapability is set to .available.

That's all the code it takes to enable our first cheat, but I'm afraid that you can test it only if you have a 3D Touch-capable device – Xcode's iOS simulator does not support 3D Touch, so either you test with a real device or just take my word for it!

Note: in case you were wondering, that code will indeed run every time the user moves their finger, but like I said earlier "UIImage shares image data across image views very efficiently, so there's no extra cost to this approach." The same is true here: this code will run very quickly.

BUILD THE ULTIMATE PORTFOLIO APP Most Swift tutorials help you solve one specific problem, but in my Ultimate Portfolio App series I show you how to get all the best practices into a single app: architecture, testing, performance, accessibility, localization, project organization, and so much more, all while building a SwiftUI app that works on iOS, macOS and watchOS.

Get it on Hacking with Swift+

Sponsor Hacking with Swift and reach the world's largest Swift community!

Buy Pro Swift Buy Pro SwiftUI Buy Swift Design Patterns Buy Testing Swift Buy Hacking with iOS Buy Swift Coding Challenges Buy Swift on Sundays Volume One Buy Server-Side Swift Buy Advanced iOS Volume One Buy Advanced iOS Volume Two Buy Advanced iOS Volume Three Buy Hacking with watchOS Buy Hacking with tvOS Buy Hacking with macOS Buy Dive Into SpriteKit Buy Swift in Sixty Seconds Buy Objective-C for Swift Developers Buy Beyond Code

Was this page useful? Let us know!

Average rating: 4.8/5

Unknown user

You are not logged in

Log in or create account

Link copied to your pasteboard.