Check if 3D touch is supported and enabled on the

2019-01-17 22:43发布

问题:

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.

回答1:

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;


回答2:

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");
    }
}


回答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
}


回答4:

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.



回答5:

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
      }
    }
  }


回答6:

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
}