10

我正在尝试将一些 3D 触摸合并到应用程序中,但遇到了一个奇怪的问题,即forceTouchCapability检查返回但nil不在.viewDidLoadviewWillAppear/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];
   }
}

if语句处有断点的 LLDB 中,输入po [self.traitCollection forceTouchCapability]返回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因为这可能会被调用很多次。

如果有帮助,我将在带有 Xcode 7.2 的 iOS 9.1 上的 6S 上运行它

4

3 回答 3

22

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

(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 设置,您的视图将被注册/取消注册。

于 2016-01-28T20:03:07.487 回答
2

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

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
}
于 2016-06-23T12:59:26.667 回答
-1

就像@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)
            }
        }
    }
}
于 2016-06-27T14:16:51.400 回答