Touch ID是iOS8上新公开的API,关于详细介绍和用法可以看CocoaChina的这两篇文章:上 和 下,在此篇文章中不再赘述。
我在app中需要的效果是如果touch id验证通过,则页面push到下一个viewController,否则本视图的数字密码输入框becomeFirstResponder。研究过touch id的人应该知道,这段代码大概会这么实现:
LAContext *context = [[LAContext alloc] init];
NSError *error; BOOL canUse = [context canEvaluatePolicy: LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&error];
if (canUse) {
[context evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics
localizedReason:@"使用touch ID来打开"
8 reply:^(BOOL success, NSError *authenticationError) {
if (success) {
//push视图
} else {
//弹出输入框
}
}];
}
我一开始确实也是这么写的,结果发现,验证完成后,无论是否成功,界面都会卡了5秒以上才会进行下一步。这显然不科学啊,然后仔细看了一下这个方法的说明,在网上搜索一番,最后在*的帮助下才弄清。一晚上时间的成果。。。。
我们先看一下- (void)evaluatePolicy:(LAPolicy)policy localizedReason:(NSString *)localizedReason reply:(void (^)(BOOL success, NSError *error))reply这个方法对最后一个参数reply这个block的描述:
reply
Reply block that is executed when policy evaluation finishes. This block is evaluated on a private queue internal to the framework in an unspecified threading context. You must not call canEvaluatePolicy:error: in this block, because doing so could lead to deadlock.
注意第二句话,大概意思是(英语不是很好,轻喷):这个block会在framework内部的一个私有队列中进行判断,而这个framework更是在一个不确定的线程中。。。说简单点就是,这个block就不在主线程中执行啊有木有!刷新界面的操作必须要放在主线程中啊有木有!意识到这点再回过头来看网上的那些讲解包括官方Demo,都只是在reply中NSLog啊、println啊、给变量赋值啊,让我抄的时候完全忽视线程这个东西。。。。
接下来就容易解决了,要不就定义一个是否验证成功的BOOL flag,在reply里判断赋值,然后紧跟着在方法外判断flag来进行下一步;要不就直接点,把reply里的操作放到主线程里,形如
[context evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics
localizedReason:@"使用touch ID来打开"
3 reply:^(BOOL success, NSError *authenticationError) {
dispatch_async(dispatch_get_main_queue(), ^{
if (success) {
//push
} else {
//弹出输入框
}
});
}];