iOS应用界面切换
- 1、UIViewController 的生命周期;
- 2、push & pop;
- 3、presentModalView. (它是和 navigationController 无关的,是在所在的 viewController 添加一个模态 view)
iOS三种则视图切换的原理各不相同 (from:Kenshin Cui's Blog)
- UITabBarController: 以平行的方式管理视图,各个视图之间往往关系不大,每个加入到UITabBarController的视图都会进行初始化,即使当前不显示在界面上
这两句现在还不是很懂
,相对比较占用内存优化的一个入口吗?
。[tabBarItem 的 image 属性必须是png格式(建议32*32)并且打开alpha通道,否则无法正常显示] - UINavigationController: 以栈的方式管理视图,各个视图的切换就是压栈和出栈操作,出栈后的视图会立即销毁
释放比较合适
。[只有在栈顶的控制器能够显示在界面中。UINavigationController默认也不会显示任何视图,它必须有一个根控制器rootViewController,而且这个根控制器不会像其他子控制器一样会被销毁。] - UIModalController: 以模态窗口的形式管理视图,当前视图关闭前,无法在其它的视图上进行操作。
对其blog进行研读并在以后做好笔记工作
(接下来先了解2和3的内容
)
下面的代码了解push & pop 和 presentModalView 的方法。
在实现文件 .m 的 viewDidLoad 方法中输入以下代码:
"1号“代码段
UIBotton *pushButton = [UIButton buttonWithType: UIButtonTypeCustom];
pushButton.frame = CGRectMake(10, 74, self.view.bounds.width - 20, 44);
[pushButton setBackgroundColor: [UIColor cyanColor]];
[pushButton setTittle: @"push a view" forState: UIControlStateNormal];
[pushButton setTitleColor: [UIColor blackColor] forState: UIControlStateNormal];
[pushButton addTarget: self
action: @selector(pushButtonClicked)
forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview: pushButton];
接下来创建一个 presentButton,目的是为了演示 两种不同的页面切换方式:
“2号”代码段
UIBotton *presentButton = [UIButton buttonWithType: UIButtonTypeCustom];
presentButton.frame = CGRectMake(10, 130, self.view.bounds.width - 20, 44);
[presentButton setBackgroundColor: [UIColor yellowColor]];
[presentButton setTittle: @"present a modal view" forState: UIControlStateNormal];
[presentButton setTitleColor: [UIColor blackColor] forState: UIControlStateNormal];
[presentButton addTarget: self
action: @selector(presentButtonClicked)
forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview: presentButton];
上面有两段 选择器 selector 的代码。通过它们使界面进行跳转,不过首先要选定跳转到哪一个页面,在选定之后,在实现文件 .m 开头导入跳转后界面的控制器才会在执行方法后可以显示界面:
“3号”代码段
#import "BLSubViewController.h"
接下来,先介绍下 push 操作:它类似于 UINavigationController 的压栈(先进后出)。下面演示上面两个 Button 所关联的 selector 跳转方法,第一个介绍的是 push (注意它们要跳转到的那个页面已经选定,就是刚导入的 BLSubViewController):
“4号”代码段
#pragma mark - Custom event methods
- (void)pushButtonClicked:(id)sender
{
BLSubViewController *subViewController = [[BLSubViewController] init];
[self.navigationController pushViewController:subViewController animated:YES];
}
也许在 animated: 中使用 YES 显得更贴近自然语言吧。
上面方法中的两行代码就已经通过 push 将页面进行了跳转。 [首先创建了一个跳转目的界面的视图控制器,然后由当前界面视图控制器 push 到 目的地视图控制器]
上面的一个代码段介绍了 push 的方法,在跳转到 BLSubViewController 的界面之后,我们如果这时需要跳回到刚才那个页面,其时就是将刚刚押入的栈弹出(所谓的pop方法)。[其实,苹果已经帮我们在BLSubViewController 的界面设置了跳转回去的按钮,如下图:
现在,我们需要自己来做一个 pop 回去,这样才算知道所以然.
首先第一步和前面设置两个 button 控件一样的,先在 BLSubViewController 的界面 复制一样的代码,改掉相关的代码,selector 方法名 设置为:backButtonClicked:
“5号”代码段
- (void)backButtonClicked:(id)sender
{
[self.navigationController popViewControllerAnimated:YES];
}
只方法中的一行代码就将其移除了。
值得注意的是在 navigationController 中还为我们提供了另一个可以返回指定 UIViewController 的方法(所以它本身也是一个数组对象 NSArray):
**NSArray popToViewController:(UIViewController ) animated:(BOOL)
还有一个是返回 rootViewController:
NSArray popToRootViewControllerAnimated:(BOOL)
一般是上面代码段中的方法更加普遍使用,我个人认为比较中庸,但也实在。后面两个方法则是比较有针对性。
上面我们将文章一开头的 界面切换 中的第二点 push & pop 讲完了,现在来讲讲第三点 presentModalView。modalView是一个模态窗口(模态的你只能在该窗口进行操作,否则就是非模态窗口),它是盖在其它视图之上的。现在我们来完成 2号代码段 中的 selector :
- (void)presentButtonClicked:(id)sender
{
BLSubViewController *subViewController = [[BLSubViewController alloc] init];
[self presentViewController:subViewController animated:YES completion:nil];
// 第二行代码中 self presentViewController: 由这个方法可以感知到,presentModalView 确实就是覆盖其上的一个视图控制器。
}
设置好之后,跳转后画面如下,presentModalView 即图中右边视图。注意 presentViewController:subViewController animated:YES completion:nil 是苹果的一个新方法,它代替了旧方法,当我们使用新方法的时候需要注意它是支持哪个版本的,根据自己项目的受众群体进行合理的设置,以免造成使用旧版系统的用户的应用崩溃。
左边显示的是 push 之后的界面,右边显示的是模态窗口。此时,在模态窗口中点击 back 是不会有任何反应的,因为此时在 BLSubViewController 中的 navigationController 的值是 nil。那么,问题来了,当进入这个模态窗口之后我们点击 back 是无效的,我们该如何退出这个窗口呢?我们需要使 back 生效,修改 “5号”代码段 :
“6号”代码段
- (void)backButtonClicked:(id)sender
{
if (self.navigationController) {
[self.navigationController popViewControllerAnimated:YES];
} else {
[self dismissViewControllerAnimated:YES completion:nil];
}
}
这里使用了 ' dismissViewControllerAnimated: completion: '。至此,点击模态窗口的 back 按钮就可以相应并跳转回原先的界面了。 同时界面切换的第三点也讲完了。
接下来,我们需要了解界面切换的第一点 UIViewController 的生命周期。
我们看到先在 BLSubViewController.m 文件中设置这些代码段:
#pragma mark - Memory management methods
......
#pragma mark - View's lifecycle methods
......
#pragma mark - Custom event methods
......
这样这个项目中的各个BL...ViewController.m 实现文件中的结构就是由这样三个代码块构成的。在 View's lifecycle methods 中的方法 - (void)viewDidLoad{} 和 - (void)viewDidUnload{} 其实都不止调用一次。例如:
在上图中,下面的五个 tabbar 点击其中一个就跳转到点击方的界面,这样该界面就被 viewDidLoad 了一次。 苹果的做法很聪明,一般,只有等你点击了下面其中一个之后,系统才会调用加载那一个视图控制图。当出现内存不够的情况(例子:在点击并加载第三个视图的时候用模拟器模拟内存警告的情况),前面调用加载的 one 和 two 视图 会调用 - (void)didReceiveMemoryWarning {} 方法,接着会调用 - (void)viewDidUnload {} 的方法(
现在这个方法已经不被苹果使用,当然你可以自己设置
),当调用了这个方法,此方法所在的 试图控制器就会被释放(比如先打开了one->two->three, 到 three 出现内存警告,three的图片不会被释放,因为用户正在使用,不过为了内存空间 one & two 就会被释放)。然后再次点击 one 或者 two 视图 ,它们会再次调用 - (void)viewDidLoad {} 的方法。
因此根据上面的理论结合实践的论述,当在运行app的时候,会根据不同的手机,以及手机不同的内存情况,系统有可能会不止一次的调用 - (void)viewDidLoad {} 和 - (void)viewDidUnload {} 方法。
不过需要说明的是这个流程只在 iOS 6 之前是存在的。在 iOS 6 之后 - (void)viewDidUnload {} 是被弃用了。不过,我们花这么大的篇幅来介绍,是有助于了解 iOS 的内存是如何进行管理的。
那现在我们的疑问是,苹果为什么会弃用这个内存管理方法呢?在听课的我也带着这个苦恼,还好苹果给出了解释: 因为当我们为了内存空间 调用了 - (void)viewDidUnload {} 的方法,是将这个 view 给置 空 了, 但这其时并没有为内存留出多少空间,这个view所占用的空间其实是很小的,所以便废弃了。 好吧,似乎是很有道理,不过我也不知道是哪里更占用所谓更多的内存。 不过有一点明确的是,以前在 - (void)viewDidUnload {} 中做的事情,就需要在 - (void)didReceiveMemoryWarning {} 中进行操作。
在 iOS6 之后,当内存释放的情况,在这个 viewDidLoad 中的view 并不会被置空,所以我们可以理解为,在那之后 - (void)viewDidLoad {} 方法只会被调用一次。
7 号代码段
#pragma mark - Memory management methods
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
if (self.view.window == nil) {
}
}
- (void)dealloc
{
}
这之后,当应用收到了内存不够的警告后,需要在- (void)didReceiveMemoryWarning {}方法中做上述代码段中的判断,只有当 self.view.window 等于空的时候,才在里面输入你要释放的一些内容(图片等数据,即同时注意不要将用户在使用的界面view给释放了!)。
emsp;上面讲的是内存释放的一些流程,现在接下来再继续讲生命周期的内容,看下面一般完整的生命周期代码段:
8 号代码段
#pragma mark - View's lifecycle methods
- (void)viewDidLoad
{
[super viewDidLoad];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
}
当我们在主界面选择点击 push a view 跳转到 BLSubViewController 后,会先后调用 代码块8 的前三个方法,当我们在 BLSubViewController 中点击 back 按钮,就会依次调用后两个方法,并在离开此界面的同时,可能会进行一些释放或者存储的功能,这样还会调用到 7号代码段的 - (void)dealloc{}方法。
上述讲的就是我们所说的 UIViewController 的生命周期。
这是第一篇完整的使用 Markdown 来写的一篇笔记,同时一篇认真的笔记的完成确实是很耗费时间的,但在耗费时间的同时也发现,这样对于理解的深入是很有帮助的。