我在我的应用程序中添加了 ios-8 的新 touchID API。它通常按预期工作,但是当我的手指已经在主页按钮上时进入应用程序时 - 调用 API 的成功回调但弹出窗口仍然出现在屏幕上。按下 CANCEL 后 UI 变得无响应。
4 回答
我也遇到了同样的问题,解决方法是使用高优先级队列调用 Touch ID API,以及延迟:
// Touch ID must be called with a high priority queue, otherwise it might fail.
// Also, a dispatch_after is required, otherwise we might receive "Pending UI mechanism already set."
dispatch_queue_t highPriorityQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.75 * NSEC_PER_SEC), highPriorityQueue, ^{
LAContext *context = [[LAContext alloc] init];
NSError *error = nil;
// Check if device supports TouchID
if ([context canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&error]) {
// TouchID supported, show it to user
[context evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics
localizedReason:@"Unlock Using Touch ID"
reply:^(BOOL success, NSError *error) {
if (success) {
// This action has to be on main thread and must be synchronous
dispatch_async(dispatch_get_main_queue(), ^{
...
});
}
else if (error) {
...
}
}];
}
});
在测试我们的应用程序时,我们发现 750 毫秒的延迟是最佳的,但您的里程可能会有所不同。
更新(2015 年 3 月 10 日):一些 iOS 开发人员,例如 1Password,报告说 iOS 8.2 终于修复了这个问题。
虽然使用延迟可以潜在地解决问题,但它掩盖了根本原因。您需要确保仅当应用程序状态为活动时才显示 Touch ID 对话框。如果您在启动过程中立即显示它(这意味着应用程序在技术上仍处于非活动状态),则可能会出现这些类型的显示问题。这没有记录在案,我很难找到这一点。提供延迟似乎可以解决它,因为那时您的应用程序处于活动状态,但这并不能保证。
为了确保它在应用程序处于活动状态时运行,您可以检查当前应用程序状态,并立即运行它,或者在我们收到 applicationDidBecomeActive 通知时运行它。请参阅下面的示例:
- (void)setup
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(applicationDidBecomeActive:)
name:UIApplicationDidBecomeActiveNotification
object:nil];
}
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
// We need to be in an active state for Touch ID to play nice
// If we're not, defer the presentation until we are
if([UIApplication sharedApplication].applicationState == UIApplicationStateActive)
{
[self presentTouchID];
}
else
{
__weak __typeof(self) wSelf = self;
_onActiveBlock = ^{
[wSelf presentTouchID];
};
}
}
-(void)applicationDidBecomeActive:(NSNotification *)notif
{
if(_onActiveBlock)
{
_onActiveBlock();
_onActiveBlock = nil;
}
}
- (void)presentTouchID
{
_context = [[LAContext alloc] init];
_context.localizedFallbackTitle = _fallbackTitle;
[_context evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics
localizedReason:_reason
reply: ^(BOOL success, NSError *authenticationError)
{
// Handle response here
}];
}
这个公认的答案没有解决问题的根本原因:调用 evaluatePolicy() 两次,第二次是在第一次调用正在进行时。因此,当前的解决方案有时只能靠运气起作用,因为一切都取决于时间。
解决该问题的蛮力、直接的方法是一个简单的布尔标志,以防止在第一次调用完成之前发生后续调用。
AppDelegate *delegate = [[UIApplication sharedApplication] delegate];
if ( NSClassFromString(@"LAContext") && ! delegate.touchIDInProgress ) {
delegate.touchIDInProgress = YES;
LAContext *localAuthenticationContext = [[LAContext alloc] init];
__autoreleasing NSError *authenticationError;
if ([localAuthenticationContext canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&authenticationError]) {
[localAuthenticationContext evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics localizedReason:kTouchIDReason reply:^(BOOL success, NSError *error) {
delegate.touchIDInProgress = NO;
if (success) {
...
} else {
...
}
}];
}
我开始收到“已设置待定 UI 机制”。也提到了错误,所以我决定看看其他应用程序是否受到影响。我为 Touch ID 设置了 Dropbox 和 Mint。果然,Touch ID 也对他们不起作用,他们开始使用密码。
我重新启动了我的手机,它又开始工作了,所以看起来 Touch ID 可能会出错并停止工作。我在 iOS 8.2 顺便说一句。
我想处理这种情况的正确方法就像那些应用程序一样并回退到密码/密码。