Check if 3D touch is supported and enabled on the iOS9 device
Asked Answered
I

6

14

I tried to access the trait collection and check "forceTouchCapability", but "forceTouchCapability" simply checks to see if the device is iOS 9.0 or greater.

So, this means that on any device with iOS 9, force touch is 'available'. I need to a way to check if 3D touch is actually supported on the users device (iPhone 6s) and I need to make sure that the 3D Touch option is actually enabled in the accessibility settings.

Incompressible answered 28/9, 2015 at 0:54 Comment(11)
That's bad news - have you tried it on the 9.1 beta?Hayton
No, we are adding 3D touch capability to an app we are going to be releasing in a few days, so we are aiming for 9.0. Using Xcode 7.0.Incompressible
Maybe I am doing something wrong.Incompressible
From what I understand, you are on the right track with accessing the trait collection and using forceTouchCapability. This link seems helpful/relevant: pinkstone.co.uk/…Insphere
On what basis are you stating that forceTouchCapability only checks to see if the device is iOS 9 or greater?Arnone
We have 3 devices - an iPad Air 1 (No 3d touch, running iOS 9.0), iPhone 6 (No 3d touch, running iOS 8.3), iPhone 6s (3d touch, running iOS 9.0).Incompressible
forceTouchCapability returns: true on iPad, true on iPhone 6s, false on iPhone 6.Incompressible
I am writing a plugin for a Unity3D game. To get access to the Unity View, I am fetching the last view using this: UIView *UnityView = [[[[UIApplication sharedApplication] keyWindow] subviews] lastObject]; and returning this: [[UnityView traitCollection] forceTouchCapability];Incompressible
I think I just realised my error - I am returning forceTouchCapability, assuming it is a 'bool'. It is actually an enum, and I guess it was being cast to a bool - returning true when it equalled '1' (UIForceTouchCapabilityUnavailable = 1)Incompressible
I was just going to point out that the return value is not a BOOL.Arnone
Amateur mistake :-) I've answered my own questionIncompressible
I
24

I was accidentally casting forceTouchCapability to a BOOL (using it as a return value to my method that was set to return a boolean). I needed to check if forceTouchCapability was equal to UIForceTouchCapabilityAvailable.

Instead of:

return [[MyView traitCollection] forceTouchCapability];

I need:

return [[MyView traitCollection] forceTouchCapability] == UIForceTouchCapabilityAvailable;
Incompressible answered 28/9, 2015 at 2:10 Comment(4)
Keep in mind that you need to properly guard this code if your app supports iOS 8.x or earlier in addition to iOS 9.Arnone
Yep! Thanks, already done :) Ran into that properly already hehe.Incompressible
swift version #33687140Underwater
@Incompressible hey I do not have a 3D touch device I wanted to ask: Although it returns NotAvailable for device not supporting 3D Touch, does it also return NotAvailable for devices with 3D touch but disabled in settings?Profluent
O
12

If you implement this in UIViewController, the timing matters. Checking in viewDidLoad will return Unknown when it will return Available later in the lifecycle.

- (void)traitCollectionDidChange:(nullable UITraitCollection *)previousTraitCollection {
    [self checkForForceTouch];
}

- (void)checkForForceTouch {
    if ([self.traitCollection respondsToSelector:@selector(forceTouchCapability)] &&
        self.traitCollection.forceTouchCapability == UIForceTouchCapabilityAvailable) {
        NSLog(@"Force touch found");
    }
}
Occultation answered 12/1, 2016 at 3:26 Comment(2)
it 3d touch disable from settings, how to check thisToadinthehole
@Toadinthehole This method will automatically detect this.Boloney
P
3

Sometimes we want just to have forceTouchCapability value right now synchronously and don't want to wait for traitCollectionDidChange: event. In that case, we can use pretty stupid function like this:

public func forceTouchCapability() -> UIForceTouchCapability {
    return UIApplication.sharedApplication().keyWindow?.rootViewController?.traitCollection.forceTouchCapability ?? .Unknown
}
Perlie answered 14/6, 2016 at 21:4 Comment(0)
M
3
if ([MyView respondsToSelector:@selector(traitCollection)] &&
    [MyView.traitCollection respondsToSelector:@selector(forceTouchCapability)] &&
    MyView.traitCollection.forceTouchCapability == UIForceTouchCapabilityAvailable) {
        return YES;
    }

My point is, if you are supporting iOS 7 and iOS 8 as well, remember to check for both the conditions: [MyView respondsToSelector:@selector(traitCollection)] and [MyView.traitCollection respondsToSelector:@selector(forceTouchCapability)].

If you keep the first check, the app works fine on iOS 7 but crashes on iOS 8. Basically, Apple introduced traitCollection property in iOS 8 but added forceTouchCapability property only in iOS 9.

from UITraitCollection.h:

@property (nonatomic, readonly) UITraitCollection *traitCollection NS_AVAILABLE_IOS(8_0);

@property (nonatomic, readonly) UIForceTouchCapability forceTouchCapability NS_AVAILABLE_IOS(9_0);

PS: Learnt it the hard way, after app started to crash on App Store.

Marlowe answered 22/1, 2017 at 18:36 Comment(0)
S
0

Swift 4.0 and 4.1.

import UIKit

class ViewController: UIViewController {
     override func viewDidLoad() {
        super.viewDidLoad()
        // Check device supports feature
        if self.traitCollection.forceTouchCapability == .available {
        // Enable 3D Touch feature here
         } else {
        // Fall back to other non 3D feature
         }
      }
      override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
        // Update the app's 3D Touch support
      if self.traitCollection.forceTouchCapability == .available {
          // Enable 3D Touch feature here
      } else {
          // Fall back to other non 3D feature
      }
    }
  }
Sisal answered 19/9, 2018 at 6:7 Comment(0)
P
0

following Valentin Shergin, updated for swift 4.2 if You need a sync call:

func forceTouchCapability() -> UIForceTouchCapability {
    return UIApplication.shared.keyWindow?.rootViewController?.traitCollection.forceTouchCapability ?? .unknown
}


func hasForceTouchCapability() -> Bool {
    return forceTouchCapability() == .available
}
Phenica answered 4/11, 2018 at 10:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.