Warning: Attempt to present A on B whose view is not in the window hierarchy!
昨天写豆瓣发广播Demo的时候,为了写Demo的简单,就使用了Storyboard,结果执行视图跳转时遇到了这个问题:
Warning: Attempt to present <UINavigationController: 0x8d514e0> on <OAuthViewController: 0xa044a60> whose view is not in the window hierarchy!
其功能是OAuthViewController用于用户授权,在获取用户的授权后将拿到的access_token通过NSUserDefaults保存起来。那么在下一次打开程序时,首先判断access_token是否已经存在,如果已经存在就直接跳转到UINavigationController。代码如下:
- (void)viewDidLoad {
[super viewDidLoad];
spinner_view.hidesWhenStopped = YES;
[webView setDelegate:self];
[webView setScalesPageToFit:YES];
// 如果已经保存了授权用户的access_token,那么直接跳转到UINavigationController
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
access_token = [userDefaults objectForKey:@"access_token"];
if (access_token) {
[self performSegueWithIdentifier:@"gotoSay" sender:nil];
return;
}
NSString *paramClientID = [NSString stringWithFormat:@"client_id=%@", API_KEY];
NSString *paramRedirect_uri = [NSString stringWithFormat:@"redirect_uri=%@", REDIRECT_URI];
NSString *paramResponse_type = @"response_type=code";
NSString *paramScope = @"shuo_basic_r,shuo_basic_w,douban_basic_common"; // 在其它API调用时可能要扩展该作用域参数
NSString *getAuthCode = [NSString stringWithFormat:@"%@?%@&%@&%@&%@", GET_AUTHORIZATION_CODE_URL, paramClientID, paramRedirect_uri, paramResponse_type, paramScope];
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:getAuthCode]];
[spinner_view startAnimating];
[webView loadRequest:request];
}
问题出现了,无法完成视图的跳转,但是用户的access_token已经在preferences的plist文件中成功保存了。控制台输出的错误信息如上所示。
查了一下资料,发现了问题所在(StackOverflow上面的高手真的很多啊),先给出两个对我很有帮助的网址:
[ios开发异常]whose view is not in the window hierarchy!
loadView、viewDidLoad、viewWillAppear、viewDidAppear等详解
解决方法:
将验证access_token是否已经存在的代码转移到viewDidAppear方法中:
- (void)viewDidAppear:(BOOL)animated {
// 如果已经保存了授权用户的access_token,那么直接跳转到UINavigationController
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
access_token = [userDefaults objectForKey:@"access_token"];
if (access_token) {
[self performSegueWithIdentifier:@"gotoSay" sender:nil];
return;
}
}
分析:
相信进行单步调试的程序员都知道,只有就viewDidLoad方法完全执行完毕,才会真正在window中加载对应的界面。在修改前的程序中就有问题出现了,注意该动作发生在A的viewDidLoad方法中:
[self performSegueWithIdentifier:@"gotoSay" sender:nil];
作用是从A跳转到B中。
那么在segue调用后,程序将调用B的viewDidLoad方法,在B的viewDidLoad方法执行完后,又回到了A的viewDidLoad的方法中执行剩下的语句。那么在window加载视图时其层次结构便发生了混乱,于是报错(当然如果从A跳转到B后面还有语句的话,最后加载的还是A的view)。
viewDidAppear方法和viewDidLoad方法的区别在于:viewDidLoad方法调用时视图还没完全过渡到window中,viewDidAppear方法调用时,视图已经完全过渡到window中了。
所以在viewDidAppear方法中调用performSegue方法实现视图跳转就不会出现以上的问题了,因为程序将有序地先执行A的viewDidLoad方法,在该方法完结后,再在viewDidAppear方法中执行segue跳转并执行B的viewDidLoad方法,这样就不会发生混乱了。
当我想从一个VC跳转到另一个VC的时候,一般会用
- (void)presentViewController:(UIViewController *)viewControllerToPresent animated: (BOOL)flag completion:(void (^)(void))completion;当然也可以用导航push。
可是昨天就遇到了题目中的warning,在stackoverflow找到了答案:点击打开链接
大意就是页面跳转必须在viewDidLoad和viewDidAppear之后才能进行。解决的办法就是确保页面跳转要在view load完毕之后进行。大神说可以通过设置延时来实现,通过实验我觉得这个不好控制,到底需要延时多少呢》
后来换了一个方案,在viewDidLoad里用
[selfperformSelectorOnMainThread:@selector(login)withObject:nilwaitUntilDone:NO];
把页面跳转的代码写进函数里,然后将 waitUntilDone 设为NO,就是viewDidLoad直接返回不用等login执行。这样就可以确保login里的页面跳转是在viewDidLoad之后执行。
另外,我还遇到下面这个情况:A页面跳转到B,在B里又封装了一个页面跳转(至C)。
也就是在前面的login函数里,从A跳转至B时会传入token、secret等用来获取用户授权的数据,然后在B里再调各大公司封装好的授权页面(如百度网盘、新浪微博)。
一开始我的login实现如下:
-(void)login
{
loginViewController *loginVC=[[loginViewControlleralloc] init];
loginVC.delegate=self;
[self presentViewController:loginVC animated:NOcompletion:nil];
[loginVC auth:BAIDU_API_KEY secret:nil disk:BAIDU];//这个函数里有跳转至授权页面
}
这同样会出现上面的问题,解决的办法就是把最后一行的函数放在completion的block里。
-(void)login
{
loginViewController *loginVC=[[loginViewController alloc] init];
loginVC.delegate=self;
[self presentViewController:loginVC animated:NO completion:^{[loginVC auth:BAIDU_API_KEY secret:nil disk:BAIDU];}];
}
因为:
The completion handler, if provided, will be invoked after the presented controllers viewDidAppear: callback is invoked.
Warning: Attempt to present A on B whose view is not in the window hierarchy!的更多相关文章
- Warning: Attempt to present on whose view is not in the window hierarchy!
当我想从一个VC跳转到另一个VC的时候,一般会用 - (void)presentViewController:(UIViewController *)viewControllerToPresent a ...
- Warning: Attempt to present * on * which is already presenting *
Warning: Attempt to present (要被presented的控制器) on (哪个控制器来presenting) which is already presenting (已经 ...
- 错误:Warning: Attempt to present <UIAlertController: 0x7fd192806e20> on <ViewController: 0x7fd1928048d0> whose view is not in the window hierarchy!
系统:mac OS 10.12 (16A323) Xcod:8.3.3 错误:Warning: Attempt to present <UIAlertController: 0x7fd1928 ...
- Attempt to present <TestViewController2: 0x7fd7f8d10f30> on <ViewController: 0x7fd7f8c054c0> whose view is not in the window hierarchy!
当 storyboard里面的 按钮 即连接了 类文件里面的点击方法 又 连接了 storyboard里 另一个 控制器的 modal 就会出现类似Attempt to present & ...
- swift一次 Attempt to present on whose view is not in the window hierarchy的解决方法
做的是二维码扫描,扫描后识别为URL的话就跳转到webview 加载网页,用的是代理传值的方式.扫描到了 值传递到主页 扫描窗体退出,检测值是否是http://开头 是网页就跳转. 问题出在传值到主界 ...
- Warning: Attempt to present on whose view is not in模态跳转问题
错误分析: controller A present controller B ,前提是A的view要存在,如果不存在,就会报这个错. 解决方法: 将 ...
- iOS8 iPad Warning: Attempt to present <UIImagePickerController:xxxx > on xxxx which is already presenting (null)
解决方法: /* I think this is because in iOS 8, alert views and action sheets are actually presented view ...
- Attempt to present <vc> on <vc> which is already presenting <vc>/(null)
在给 tableViewCell 添加长按手势弹出一个 popViewController 的时候,遇到的这个变态问题: Warning: Attempt to present <UINavig ...
- 跳转时候提示Attempt to present on while a presentation is in progress
出现这种情况,例如:我在获取相册图片后,直接present到另一个页面,但是上一个页面可能还未dismiss,所以,要在获取相册图片的dismiss方法的complete的block里面写获取图片及跳 ...
随机推荐
- 用 Django 做了一个照片分享网站
最近翻了一下过去做过的东西,找到了这个绿光照片分享,于是就拿来分享了.项目地址在: https://github.com/restran/green-glow 这是我2012年的一个课程作业,实现的功 ...
- sqlserver导入导出数据库结构及创建用户分配权限
1.创建用户分配权限 https://www.cnblogs.com/jennyjiang-00/p/5803140.html 2.sqlserver2008导出表结构和表数据 导出表结构 htt ...
- hbase表的多版本读写
TTL(Time To Live)生存期 hbase表默认保存一个版本的数据 hbase(main):123:0> create 't_name','st1'Created table t_na ...
- js 断点调试
- 根据PID寻找程序源位置--lsof
ulimit其实就是对单一程序的限制,进程级别的 file-max是所有时程最大的文件数 nr_open是单个进程可分配的最大文件数 确认系统设置的最大文件句柄数 ulimit -a 统计系统中当前打 ...
- ADO数据库编程入门
ADO 是目前在Windows环境中比较流行的客户端数据库编程技术. ADO是建立在OLE DB底层技术之上的高级编程接口,因而它兼具有强大的数据处理功能(处理各种不同类型的数据源.分布式的数据处理等 ...
- Haskell语言学习笔记(36)Data.List.Zipper
ListZipper 模块 $ cabal install ListZipper Installed ListZipper-1.2.0.2 Prelude> :m +Data.List.Zipp ...
- hivepython 实现一行转多行
案例1: ==效果等同于一行转多行 数据表名称:zhangb.gid_tags 数据格式,每行是2个字段,(gid,tags) ,可能有脏数据,分隔符为“\t”, ANDROID-9de77225 ...
- ContextLoaderListener和Spring MVC中的DispatcherServlet学习
DispatcherServlet介绍 DispatcherServlet是Spring前端控制器的实现,提供Spring Web MVC的集中访问点,并且负责职责的分派,与Spring IoC容器无 ...
- RN项目中关于父子组件的通信
子组件向父组件传递数据 子控件中在相应的函数中.通过props.coallback的回调通知父组件. 父组件调用callback属性时行 绑定,并在方法中去解析使用获取到的值 . //子控件: < ...