使用MBProgressHUD引发"self.navigationController.view为nil"的血案

来源:互联网 发布:php电商开源系统 2017 编辑:程序博客网 时间:2024/06/04 20:08

首先介绍下开源的第三方库MBProgressHUD

一个很好的实现了加载提示框的第三方demo。广泛用于应用中。

简单看下界面


开源地址:https://github.com/jdg/MBProgressHUD


好了,本文不是介绍如何使用它。

主要说下 我在使用这斯货时遇到一个怪异问题,然后引发出的一点自我解释,或许你也会遇到的一个问题。


我们先来看下demo给的一个简单例子

- (IBAction)showSimple:(id)sender {// The hud will dispable all input on the view (use the higest view possible in the view hierarchy)HUD = [[MBProgressHUD alloc] initWithView:self.navigationController.view];[self.navigationController.view addSubview:HUD];// Regiser for HUD callbacks so we can remove it from the window at the right timeHUD.delegate = self;// Show the HUD while the provided method executes in a new thread[HUD showWhileExecuting:@selector(myTask) onTarget:self withObject:nil animated:YES];}
注意HUD是根据我们传递进去的View来实例化的(准确的说是我们实例化HUD时定义frame是根据传递进的view来生成同样大小)。

简单来说,整个HUD其实是一个父布局一样大小的类似于背景的VIew,然后才是中心那个黑色的提示框。

其实这样的设计是做一个人为的同步,在加载期间做一个人为的同步。也就是事件处理完以前,你做了交互操作。

很多时候我们确实需要这样的设计,即,在网络请求活着是用户事件返回结果前,不允许用户操作。

说一个我实际中碰到的例子:

在做上一个Android项目时,有一个金额支付跳转到支付宝的操作。

在点击确认后,会将表单信息传递到我们的服务端生成订单信息和支付金额返回,然后跳转到支付宝。

如果我们将这个操作看成是在ios执行,我点一次确认,网络请求发出,然后如果没有屏蔽用户的交互,我手贱在返回数据跳转到支付宝前又点了一次,

那你就一个货物就可能买了两次,或者一些其他的支付异常。(这确实是我android产品中遇到的,虽然android本身的dialog机制是屏蔽了其他交互的,

但是android蛋疼的存在一个back,取消dialog的按钮,因此还是会造成多次提交的问题,当然最后是进一步屏蔽掉dialog的取消事件)。

也就是总体来说,我们需要一层加载框来屏蔽交互。

对于ios中,这一层就是我们这样

[self.navigationController.view addSubview:HUD];

填上去的,对于HUD的frame,前面我们也说了,根据你传递进去的View来确定的。


因此就出现我标题所提到的一个问题了。

我的意思想屏蔽导航栏的回退。


因此我会需要frame大小理论上是self.navigationController.view大小。

于是就很顺手的传递进将其传递进去。

但是,当我从前一个界面跳转到这个界面,进行第一次加载时,会报错"View must not be nil."

什么情况!!

demo 翻来翻去,他就是这么写的啊。

然后再看一下我的流程,

在viewDidLoad中所有都初始化加载完后,调用了网络请求,然后去实例化hud。

然后我又去degub下了,发现self.navigationController居然是空的~~

就是说此刻我这个视图控制器还没有拥有对导航控制器的引用~


突然就觉得好奇怪,那我等会会用到self.navigationController push到下一个控制器,又说明它是存在的了。

因此简单猜想下整个流程机制问题:

1.我从前一个视图推push当前视图时,先实例化然后加载该控制器的所有UI界面,将这整体放入视图控制器栈中(理论上也就是成了栈顶视图控制器)。

2.导航栏控制器将这个top视图添加到window上,把自身指针传给这个top视图,使其获得导航引用以及控制权。

简单来说,viewdidload只是加载初始化了所有视图。然后添加到导航控制视图栈中,然后将其当做顶端视图控制器添加到window上

(猜测,但是大致意思应该就是这样),赋予navigationController的引用控制权。因此此刻才self.navxxxx被赋值了。

然后根据控制器的生命周期,因为我们知道,把视图的显示就是一个控制器将视图添加到window上的过程和结果。

在这一步中,视图控制器会调用viewWillAppear,以及didapp~~。

然后我重载测试后,此刻nav已经被赋值。

这也就是说,此刻你调用网络请求,hud实例化时nav不会再被报空。


说了这么多,其实真正的意思只是对导航控制的工作原理做一个自我的感性认识。

因为貌似在此之前,我还真么仔细去理解控制器,视图栈是如何运作的,而是机械性的看了simpleCode,

就说,哦,我知道这个控件啊,不就这么用么。

同样希望对一些碰到和我同样的异常的朋友有所帮助。

以上可能只是我自己对自己的一个解释,更多真实内容请参考官方文档。



原创粉丝点击