whose view is not in the window hierarchy

来源:互联网 发布:js识别android ios 编辑:程序博客网 时间:2024/05/21 05:17

今天不经意间发现一个好玩的问题,感觉这个问题挺奇葩的,而且以前没有遇到过,然后百度发现就有两个回答,还是老外的回答

http://stackoverflow.com/questions/15287678/warning-attempt-to-present-viewcontroller-whose-view-is-not-in-the-window-hiera

http://stackoverflow.com/questions/26022756/warning-attempt-to-present-on-whose-view-is-not-in-the-window-hierarchy-s# 


问题的起因在于,我在viewDidLoad方法中直接加载另一个viewcontroller。起初我把tap方法写在了viewDidLoad方法中,然后运行,发现控制台打印了

2016-07-07 15:26:44.740 CALayerFun[2492:255155] Warning: Attempt to present <UIAlertController: 0x7f995360dd40> on <LCSKeyBoardViewController: 0x7f99537aacb0> whose view is not in the window hierarchy!

这样一段话,当然模拟器也没有加载这个alert成功,没有反应。


这是什么情况,首先检查代码,代码和平时写的一样,没问题啊。唯一的不同就是以前都是通过一个点击也好,其他方式也好,来唤起这个alert。但是今天我直接在viewDidLoad中加载的这个alert。可能问题出在这。

首先看看原来的UIAlertView是不是和现在使用的UIAlertController结果一样,然后直接写了一个UIAlertView在viewDidLoad方法中。这时候运行程序,发现正常加载。。。这说明什么,这说明问题出在一个是view和一个是viewController。然后,本着眼见为实的目的,新建一个viewController也让它在viewDidLoad方法中跳转,这个时候发现和跳转UIAlertController有一样的错误,跳转不成功,打印信息也一样。

先附上代码,都是日常代码,没有任何技术含量 ~~

- (void)viewDidLoad {

    [superviewDidLoad];

    self.view.backgroundColor = [UIColorwhiteColor];

    

    //第一种,用原来的alertview(正常)

    UIAlertView *alert = [[UIAlertViewalloc]initWithTitle:@"提示"message:@"    新闻要点,最新资讯,及时更新,新闻要点,最新资讯,及时更新新闻要点,最新资讯,及时更新新闻要点,最新资讯,及时更新新闻要点"delegate:nilcancelButtonTitle:@"好的"otherButtonTitles:@"不要",nil];

    [alert show];

    

    return;

    

    //第二种,直接上来就调用(不正常)

    [selftap];


    return;

    

    //第三种,经过四秒后调用(正常)

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(4 *NSEC_PER_SEC)),dispatch_get_main_queue(), ^{

        

        [selftap];

    });

    

    return;

    

    //第四种,通过点击事件调用(正常)

    UITapGestureRecognizer *tap = [[UITapGestureRecognizeralloc]initWithTarget:selfaction:@selector(tap)];

    [self.viewaddGestureRecognizer:tap];

    

    return;


- (void)tap{


    

    UIAlertController *alert = [UIAlertControlleralertControllerWithTitle:@"提示"message:@"    新闻要点,最新资讯,及时更新,新闻要点,最新资讯,及时更新新闻要点,最新资讯,及时更新新闻要点,最新资讯,及时更新新闻要点"preferredStyle:UIAlertControllerStyleAlert];

    UIAlertAction *yesAction = [UIAlertActionactionWithTitle:@"好的"style:UIAlertActionStyleDefaulthandler:^(UIAlertAction *_Nonnull action) {

        NSLog(@"yes");

    }];

    

    UIAlertAction *noAction = [UIAlertActionactionWithTitle:@"不要"style:UIAlertActionStyleCancelhandler:^(UIAlertAction *_Nonnull action) {

        NSLog(@"no");

        

    }];

    

    [alert addAction:yesAction];

    [alert addAction:noAction];

    

    [selfpresentViewController:alertanimated:YEScompletion:^{

        

    }];


}


上效果图:


    

不正常的效果图



接下来要分析原因了,就是为什么我调用方法,和延时调用的时候可以加载,而直接加载会有问题。
先把错误信息翻译一下,唉~苦逼的四级水平,对英语还是有点反感,还好有百度~~

2016-07-07 15:40:35.734 CALayerFun[2636:268808] Warning: Attempt to present <ViewController: 0x7fb3017147a0> on <LCSKeyBoardViewController: 0x7fb30145c890> whose view is not in the window hierarchy!


警告:试图弹出<ViewController: 0x7fb3017147a0>这个视图在<LCSKeyBoardViewController: 0x7fb30145c890>上,<ViewController: 0x7fb3017147a0>的view没有在窗口的层次结构中。

然后,我猜想这是因为viewcontroller加载过程需要一个时间,而如果直接加载,导致view没有加载出来呢,还不存在,跳转当然不成功。

于是,我又把调用方法延迟4秒的那个,改成了延迟0秒

  dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0*NSEC_PER_SEC)),dispatch_get_main_queue(), ^{

        

        [selftap];

    });


然而,事情总是不是那么顺利,0秒后调用,按道理说应该和直接调用一个结果,但是并不是。如果直接调用那么不能正确加载,如果这样异步0秒后加载就能加载成功了。这是什么鬼!!!我滴天呐~~
说明这么胡乱想一通还是行不通的,不幸的是我知道这个问题了,却不知道为什么出现这个问题。虽然错误信息指向了view的层次结构乱了。

解决问题:
在controller a中跳转的时候,要在viewDidLoad以及viewDidAppear之后才能跳转,所以可以自己想办法延时跳转,而不是直接跳转,延时的话,方法就很多了,可以用dispatch_after各种等等。

7月8号
经过和一些开发人员讨论,结果都是说,a中的view没有加载出来呢,就开始展示b的view这样导致的出错。然后,我在点击事件中,加入了一行代码 self.view = nil;这样,保证了a的view进入tap点击事件后为空,然后结果打印信息一样,效果图如下:


这就是说明之前出现那个alertcontroller加载不成功,并且警告的原因在于,a的view没有加载出来呢,那么问题来了,我延迟0秒不应该view也没有加载出来吗???难道是dispatch_after方法捣的鬼?具体里边实现的问题?


然后我换了不同的延时方式来执行这个tap方法,如下图


这样看来,是线程问题了,说实话现在对多线程还是比较发怵的,因为我现在并不知道,这个延时0秒,到底做了什么,如果你知道原因,请给我评论区留言,私信,谢谢了。


7月10日

查资料和日常经验知道了这几种延迟调用一个方法的区别和联系

1.performSelector方法

此方式要求必须在主线程中执行,否则无效。是一种非阻塞的执行方式,暂时未找到取消执行的方法。

2.定时器:NSTimer

此方式要求必须在主线程中执行,否则无效。是一种非阻塞的执行方式,可以通过NSTimer类的- (void)invalidate;取消执行。

3. sleep方式

此方式在主线程和子线程中均可执行。是一种阻塞的执行方式,建方放到子线程中,以免卡住界面,没有找到取消执行的方法。

4.GCD方式

此方式在可以在参数中选择执行的线程。是一种非阻塞的执行方式,没有找到取消执行的方法。

这样看来,上面的问题指向的是不同的延迟调用方法是否为阻塞方式,如果阻塞了当前线程,那么就会出现那个错误,如果没有阻塞,那么就会执行成功。


我也不知道这样理解是否有什么出入,如果你对这个比较了解,欢迎指正,留言、私信皆可




1 0
原创粉丝点击