forceTouchCapability 返回零

forceTouchCapability returning nil

我正在尝试将一些 3D 触摸整合到应用程序中,但我 运行 遇到了一个奇怪的问题,其中 forceTouchCapability 检查在 viewDidLoad 上返回 nil但不在 viewWillAppear/viewDidAppear.

我知道这仅适用于 iOS 9+,因此我添加了检查以验证视图控制器上的 traitCollection 属性 响应 forceTouchCapability 如下所示:

- (void)loadView {

   self.view = [[MyView alloc] init];
}

- (void)viewDidLoad {

   [super viewDidLoad];

   // Checking the force touch availability here
   if ([self.traitCollection respondsToSelector:@selector(forceTouchCapability)] &&
        self.traitCollection.forceTouchCapability == UIForceTouchCapabilityAvailable) {

       // This won't get called because forceTouchCapability is returning nil 
       // which corresponds to UIForceTouchCapabilityUnknown
       [self registerForPreviewingWithDelegate:self sourceView:self.view];
   }
}

在 LLDB 中,在 if 语句处有一个断点,输入 po [self.traitCollection forceTouchCapability] returns nil 对应于 UIForceTouchCapabilityUnknown。但是,traitCollection 本身不是 nil

根据 UIForceTouchCapabilityUnknown 的文档:

UIForceTouchCapabilityUnknown: The availability of 3D Touch is unknown. For example, if you create a view but have not yet added it to your app’s view hierarchy, the view’s trait collection has this value.

此时是否还没有将视图添加到层​​次结构中?

我很好奇之前是否有人 运行 遇到过这个问题以及如何解决这个问题?我想避免在 viewDidAppear 中添加它,因为它可以被调用很多。

如果有帮助,我会 运行在 iOS 9.1 和 Xcode 7.2

的 6S 上安装这个

该视图尚未添加到视图层次结构中。您可以通过在调试控制台中检查超级视图来轻松看到这一点

(lldb) po self.view.superview
nil

如果这就是您所看到的,则该视图尚未添加到层次结构中:因此您必须在别处进行检查。

这有点令人困惑,因为在 Apple 的 ViewControllerPreview 示例应用程序中,它位于 viewDidLoad 中。但它确实应该在 traitCollectionDidChange: 中,因为那样你就可以确定该视图已添加到应用程序的层次结构中。

这是我使用的代码(适用于 iOS 8,如果您不需要支持,请随意移动外部条件)。

- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection {
   [super traitCollectionDidChange:previousTraitCollection];

   if ([self.traitCollection respondsToSelector:@selector(forceTouchCapability)]) {
       if (self.traitCollection.forceTouchCapability == UIForceTouchCapabilityAvailable) {
           // retain the context to avoid registering more than once
           if (!self.previewingContext) {
               self.previewingContext = [self registerForPreviewingWithDelegate:self sourceView:self.view];
           }
       } else {
           [self unregisterForPreviewingWithContext:self.previewingContext];
           self.previewingContext = nil;
       }
   }
}

这样做的额外好处是,如果用户在应用处于 运行 时更改其 3D Touch 设置,您的视图将为 registered/unregistered。

我也看到了这个问题,发现检查设备是否支持强制触摸的最简单方法是通过屏幕实例进行。这是有道理的,因为该功能是屏幕的 属性。这样做意味着您不必担心 viewcontroller 或视图的生命周期。

func canForceTouch() -> Bool
{
  if iOS9OrHigher // pseudocode, a function that makes sure u only do this check on ios9 or higher
  {
      return UIScreen.mainScreen().traitCollection.forceTouchCapability == .Available
  }
  return false
}

正如@bpapa 所说,您的视图尚未添加到视图层次结构中,但我的解决方案有点不同:

var token:dispatch_once_t = 0
override func viewDidAppear(animated: Bool) {
    dispatch_once(&token) { 
        // Force Touch Checking
        if #available(iOS 9.0, *) {
            if self.traitCollection.forceTouchCapability == .Available {
                self.registerForPreviewingWithDelegate(self, sourceView: self.view)
            }
        }
    }
}