presentViewController:animation:YES视图不会出现,直到用户再次点击

我得到了一些与presentViewController:animated:completion奇怪的行为presentViewController:animated:completion 。 我所做的基本上是一个猜谜游戏。

我有一个UIViewController (frequencyViewController)包含一个UITableView (frequencyTableView)。 当用户点击包含正确答案的questionTableView中的行时,视图(correctViewController)应该实例化,其视图应该从屏幕底部向上滑动,作为模态视图。 这告诉用户他们有一个正确的答案,并重置后面的frequencyViewController准备好下一个问题。 correctViewController被解除button按下显示下一个问题。

这一切都工作正常,每当correctViewController的视图立即出现,只要presentViewController:animated:completion animated:NO

如果我设置animated:YES ,correctViewController被初始化,并调用viewDidLoad 。 但是viewWillAppearviewDidAppearpresentViewController:animated:completion的完成块不会被调用。 该应用程序只是坐在那里仍然显示frequencyViewController,直到我再次点击。 现在,viewWillAppear,viewDidAppear和完成块被调用。

我调查了一下,这不只是另一个水龙头,将导致它继续。 似乎如果我倾斜或摇动我的iPhone这也可以导致它触发viewWillLoad等,就像它正在等待任何其他位用户input之前,它会进步。 这发生在一个真正的iPhone和模拟器,我通过发送摇命令模拟器certificate。

我真的不知道该怎么做…我真的很感激任何人都可以提供的帮助。

谢谢

这是我的代码。 这很简单

这是questionViewController中充当委托给questionTableView的代码

 - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { if (indexPath.row != [self.frequencyModel currentFrequencyIndex]) { // If guess was wrong, then mark the selection as incorrect NSLog(@"Incorrect Guess: %@", [self.frequencyModel frequencyLabelAtIndex:(int)indexPath.row]); UITableViewCell *cell = [self.frequencyTableView cellForRowAtIndexPath:indexPath]; [cell setBackgroundColor:[UIColor colorWithRed:240/255.0f green:110/255.0f blue:103/255.0f alpha:1.0f]]; } else { // If guess was correct, show correct view NSLog(@"Correct Guess: %@", [self.frequencyModel frequencyLabelAtIndex:(int)indexPath.row]); self.correctViewController = [[HFBCorrectViewController alloc] init]; self.correctViewController.delegate = self; [self presentViewController:self.correctViewController animated:YES completion:^(void){ NSLog(@"Completed Presenting correctViewController"); [self setUpViewForNextQuestion]; }]; } } 

这是整个correctViewController

 @implementation HFBCorrectViewController - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; if (self) { // Custom initialization NSLog(@"[HFBCorrectViewController initWithNibName:bundle:]"); } return self; } - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view from its nib. NSLog(@"[HFBCorrectViewController viewDidLoad]"); } - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; NSLog(@"[HFBCorrectViewController viewDidAppear]"); } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } - (IBAction)close:(id)sender { NSLog(@"[HFBCorrectViewController close:sender:]"); [self.delegate didDismissCorrectViewController]; } @end 

编辑:

我之前发现这个问题: UITableView和presentViewController需要2次点击才能显示

如果我改变了我的didSelectRow代码,这对于animation来说是非常didSelectRow …但是它很混乱,没有意义,为什么它不起作用。 所以我不认为这是一个答案…

 - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { if (indexPath.row != [self.frequencyModel currentFrequencyIndex]) { // If guess was wrong, then mark the selection as incorrect NSLog(@"Incorrect Guess: %@", [self.frequencyModel frequencyLabelAtIndex:(int)indexPath.row]); UITableViewCell *cell = [self.frequencyTableView cellForRowAtIndexPath:indexPath]; [cell setBackgroundColor:[UIColor colorWithRed:240/255.0f green:110/255.0f blue:103/255.0f alpha:1.0f]]; // [cell setAccessoryType:(UITableViewCellAccessoryType)] } else { // If guess was correct, show correct view NSLog(@"Correct Guess: %@", [self.frequencyModel frequencyLabelAtIndex:(int)indexPath.row]); //////////////////////////// // BELOW HERE ARE THE CHANGES [self performSelector:@selector(showCorrectViewController:) withObject:nil afterDelay:0]; } } -(void)showCorrectViewController:(id)sender { self.correctViewController = [[HFBCorrectViewController alloc] init]; self.correctViewController.delegate = self; self.correctViewController.modalTransitionStyle = UIModalTransitionStyleCrossDissolve; [self presentViewController:self.correctViewController animated:YES completion:^(void){ NSLog(@"Completed Presenting correctViewController"); [self setUpViewForNextQuestion]; }]; } 

今天我遇到了同样的问题。 我深入研究了这个话题,似乎和主要的runloop睡着了。

其实这是一个非常微妙的错误,因为如果你在代码中有丝毫的反馈animation,定时器等等,这个问题就不会出现,因为runloop会被这些源保持活着。 我已经通过使用UITableViewCellselectionStyle设置为UITableViewCellSelectionStyleNone来find问题,因此在select行处理程序运行后没有selectanimation触发runloop。

要解决它(直到苹果做一些事情),你可以通过几种方式触发主要的runloop:

最less侵入的解决scheme是调用CFRunLoopWakeUp

 [self presentViewController:vc animated:YES completion:nil]; CFRunLoopWakeUp(CFRunLoopGetCurrent()); 

或者你可以将一个空白块排入主队列:

 [self presentViewController:vc animated:YES completion:nil]; dispatch_async(dispatch_get_main_queue(), ^{}); 

这很有趣,但如果你摇晃设备,它也会触发主循环(它必须处理运动事件)。 此外,如果系统更新状态栏(例如时钟更新,WiFi信号强度变化等),也会唤醒主循环,并呈现视图控制器。

对于任何有兴趣的人,我写了一个最小的问题示范项目来validationrunloop假设: https : //github.com/tzahola/present-bug

我也向苹果报告了这个错误。

检查了这一点: https : //devforums.apple.com/thread/201431如果你不想全部阅读 – 一些人(包括我)的解决scheme是在主线程上显式调用presentViewController

 dispatch_async(dispatch_get_main_queue(), ^{ [self presentViewController:myVC animated:YES completion:nil]; }); 

可能iOS7搞乱了didSelectRowAtIndexPath的线程。

我使用下面的代码绕过了Swift 3.0:

 DispatchQueue.main.async { self.present(UIViewController(), animated: true, completion: nil) } 

在正在呈现的视图控制器上调用[viewController view]对我来说是个诡计。

我会好奇,看看[self setUpViewForNextQuestion]; 确实。

你可以尝试调用[self.correctViewController.view setNeedsDisplay];presentViewController的完成块的presentViewController

我已经写了扩展(类别)的方法swizzling解决这个问题的UIViewController。 感谢AX和NSHipster实现提示( swift / objective-c )。

迅速

 extension UIViewController { override public class func initialize() { struct DispatchToken { static var token: dispatch_once_t = 0 } if self != UIViewController.self { return } dispatch_once(&DispatchToken.token) { let originalSelector = Selector("presentViewController:animated:completion:") let swizzledSelector = Selector("wrappedPresentViewController:animated:completion:") let originalMethod = class_getInstanceMethod(self, originalSelector) let swizzledMethod = class_getInstanceMethod(self, swizzledSelector) let didAddMethod = class_addMethod(self, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod)) if didAddMethod { class_replaceMethod(self, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod)) } else { method_exchangeImplementations(originalMethod, swizzledMethod) } } } func wrappedPresentViewController(viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)?) { dispatch_async(dispatch_get_main_queue()) { self.wrappedPresentViewController(viewControllerToPresent, animated: flag, completion: completion) } } } 

Objective-C的

 #import <objc/runtime.h> @implementation UIViewController (Swizzling) + (void)load { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ Class class = [self class]; SEL originalSelector = @selector(presentViewController:animated:completion:); SEL swizzledSelector = @selector(wrappedPresentViewController:animated:completion:); Method originalMethod = class_getInstanceMethod(class, originalSelector); Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector); BOOL didAddMethod = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod)); if (didAddMethod) { class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod)); } else { method_exchangeImplementations(originalMethod, swizzledMethod); } }); } - (void)wrappedPresentViewController:(UIViewController *)viewControllerToPresent animated:(BOOL)flag completion:(void (^ __nullable)(void))completion { dispatch_async(dispatch_get_main_queue(),^{ [self wrappedPresentViewController:viewControllerToPresent animated:flag completion:completion]; }); } @end 

检查故事板中的单元格是否具有Selection = none

如果是这样,将其更改为蓝色或灰色,它应该工作