一般来说,该模式涉及使用完成块,例如:
- (void) doSomethingToGetKeyWithCompletionHandler:(void (^)(NSString *key))completion
{
// perform asynchronous request
// in its completion handler, call the `completion` block parameter given above
[api doingSomeOtherAsynchronousMethodWithCompletion:^{
completion(key);
}];
}
然后,代替:
[self doSomethingToGetKey];
[self doSomethingElseWithKey:_key];
你可以这样做:
[self doSomethingToGetKeyWithCompletionHandler:^(NSString *key){
[self doSomethingElseWithKey:key];
}];
但是,在这种情况下,您尝试在内部完成所有这些操作prepareForSegue
。这完全改变了问题,因为在调用此异步方法之前,segue 仍将执行,从而违背了此完成块模式的目的。
因此,您还想更改按钮(或其他按钮)以不执行转场本身,而是调用IBAction
,然后有that以编程方式执行segue(如图所示)。因此,你最终会得到一个IBAction
like:
- (IBAction)tappedButton:(id)sender
{
// perhaps update UI so user knows something slow is happening first,
// e.g., show a `UIActivityIndicatorView`
[self doSomethingToGetKeyWithCompletionHandler:^(NSString *key){
// remove that `UIActivityIndicatorView`, if you showed one
[self performSegueWithIdentifier:@"Details" sender:self];
}];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:@"Details"]) {
[segue.destinationViewController setKey:_key]
}
}
或者,您可以删除所有这些,只执行 segue,然后让目标视图控制器出现,显示活动指示器视图,执行 api 调用,然后删除活动指示器视图。这是对代码的更戏剧性的重构,但可能是更好的用户体验。
但是,底线是从内部prepareForSegue
即使您使用完成块模式,您也不能执行异步操作,并期望目标视图控制器等待它。你必须使用IBAction
如果您想在执行转场之前完成所有这些操作,请使用此方法。或者,就像我在这里建议的那样,只需在目标视图控制器中执行所有这些异步请求即可。由于某种原因,立即转换到目标视图控制器(并显示UIActivityIndicatorView
那里)比延迟目标视图控制器的呈现更令人满意的用户体验(即使它们在功能上非常相似)。