9、iOS应用界面切换(笔记知识源:Geekband & 网易云课堂,同时包括各类网上资源的查找)

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 的界面设置了跳转回去的按钮,如下图:

左上角的 One 就是返回的控件

现在,我们需要自己来做一个 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 是苹果的一个新方法,它代替了旧方法,当我们使用新方法的时候需要注意它是支持哪个版本的,根据自己项目的受众群体进行合理的设置,以免造成使用旧版系统的用户的应用崩溃。

9、iOS应用界面切换(笔记知识源:Geekband & 网易云课堂,同时包括各类网上资源的查找)_第1张图片
presentModalView.jpg

  左边显示的是 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{} 其实都不止调用一次。例如:

9、iOS应用界面切换(笔记知识源:Geekband & 网易云课堂,同时包括各类网上资源的查找)_第2张图片
界面1.jpg

  在上图中,下面的五个 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 来写的一篇笔记,同时一篇认真的笔记的完成确实是很耗费时间的,但在耗费时间的同时也发现,这样对于理解的深入是很有帮助的。

你可能感兴趣的:(9、iOS应用界面切换(笔记知识源:Geekband & 网易云课堂,同时包括各类网上资源的查找))