ios - presentViewController:animated:YES视图在用户再次点击之前不会出现

我正在使用presentViewController:animated:completion获得一些奇怪的行为。我所做的实际上是一个猜谜游戏。

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

这一切都可以正常工作,只要presentViewController:animated:completiondidSelectRow,就会立即显示correctViewController的视图。

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

我调查了一下,并不只是另一个水龙头会导致它继续下去。 看起来如果我倾斜或摇动我的iPhone,这也可能导致它触发viewWillLoad等。就像它正在等待任何其他位用户输入才会进展。 这发生在真正的iPhone和模拟器中,我通过向模拟器发送shake命令证明了这一点。

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

谢谢

这是我的代码。 这很简单......

这是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代码更改为此代码,它可以在动画中运行很长时间......但它很麻烦,并且无法解释为什么它首先不起作用。 所以我不认为这是一个答案......

- (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];
    }];
}
HalfNormalled asked 2019-09-11T10:16:05Z
8个解决方案
145 votes

我今天遇到了同样的问题。 我挖掘了这个主题,似乎它与主要的runloop睡着有关。

实际上这是一个非常微妙的错误,因为如果你的代码中有最轻微的反馈动画,定时器等,这个问题就不会浮出水面,因为runloop将会被这些来源保持活着。 我通过使用CFRunLoopWakeUp将其selectionStyle设置为UITableViewCellSelectionStyleNone找到了该问题,因此在行选择处理程序运行后没有选择动画触发了runloop。

要解决它(直到Apple做某事)你可以通过几种方式触发主runloop:

最少侵入性的解决方案是致电CFRunLoopWakeUp

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

或者您可以将空块排入主队列:

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

这很有趣,但是如果你摇动设备,它也会触发主循环(它必须处理运动事件)。 同样的事情与水龙头,但这包括在原始问题:)此外,如果系统更新状态栏(例如时钟更新,WiFi信号强度改变等),这也将唤醒主循环并呈现视图控制器。

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

我也向Apple报告了这个漏洞。

Tamás Zahola answered 2019-09-11T10:17:07Z
65 votes

看看这个:[https://devforums.apple.com/thread/201431]如果您不想全部阅读 - 某些人(包括我)的解决方案是在主线程上明确调用didSelectRowAtIndexPath

Swift 4.2:

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

Objective-C的:

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

可能iOS7正在搞乱didSelectRowAtIndexPath中的主题。

AXE answered 2019-09-11T10:17:52Z
7 votes

我使用以下代码在Swift 3.0中绕过它:

DispatchQueue.main.async { 
    self.present(UIViewController(), animated: true, completion: nil)
}
Mark answered 2019-09-11T10:18:18Z
2 votes

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

JaganY answered 2019-09-11T10:18:45Z
0 votes

我很想知道[self.correctViewController.view setNeedsDisplay];是做什么的。

您可以尝试在presentViewController的完成块结束时致电[self.correctViewController.view setNeedsDisplay];

Nick answered 2019-09-11T10:19:18Z
0 votes

我用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
Gladkov_Art answered 2019-09-11T10:19:48Z
0 votes

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

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

Mkey answered 2019-09-11T10:20:21Z
0 votes

XCode Vesion:9.4.1,Swift 4.1

在我的情况下,当我点击单元格并移动另一个视图时,会发生这种情况。 我调试到更深层次,似乎发生在viewDidAppear里面因为包含以下代码

if let indexPath = tableView.indexPathForSelectedRow {
   tableView.deselectRow(at: indexPath, animated: true)
}

然后我在viewDidAppear内添加了上面的代码段并且工作完美。

根据我的经验,我的解决方案是,如果我们希望在从第二个视图再次返回时为tableview做任何新的更改(例如,表重新加载,取消选择所选单元格等),那么使用委托代替viewDidAppear并使用以上 tableView.deselectRow移动第二个视图控制器之前的代码段

Sachintha Udara answered 2019-09-11T10:21:09Z
translate from https://stackoverflow.com:/questions/21075540/presentviewcontrolleranimatedyes-view-will-not-appear-until-user-taps-again