前言

	NS_CLASS_AVAILABLE_IOS(2_0) @interface UIViewController : UIResponder <NSCoding, UIAppearanceContainer, UITraitEnvironment, UIContentContainer, UIFocusEnvironment>
@available(iOS 2.0, *) public class UIViewController : UIResponder, NSCoding, UIAppearanceContainer, UITraitEnvironment, UIContentContainer, UIFocusEnvironment
  • 视图控制器负责页面的创建、事件处理等。

  • 每一个视图控制器(UIViewController)内部都有个默认的 UIView 属性,控制器中管理的其他所有控件都是这个 view 的子控件(直接或者间接)。

1、ViewController 的创建

  • Objective-C

    	// 实例化视图控制器
    UiViewController *viewController = [[UiViewController alloc] init]; // 设置 window 的根视图控制器
    self.window.rootViewController = viewController;
  • Swift

    	// 实例化视图控制器
    let viewController:UiViewController = UiViewController() // 设置 window 的根视图控制器
    self.window?.rootViewController = viewController

2、ViewController 的设置

  • Objective-C

    	// 设置 viewController 的背景颜色
    self.view.backgroundColor = [UIColor redColor]; // 向 viewController 上添加视图
    [self.view addSubview:button]; // 向 viewController 上添加视图控制器
    [self addChildViewController: viewController2];
    [self.view addSubview:viewController2.view];
  • Swift

    	// 设置 viewController 的背景颜色
    self.view.backgroundColor = UIColor.redColor() // 向 viewController 上添加视图
    self.view.addSubview(button) // 向 viewController 上添加视图控制器
    self.addChildViewController(viewController2)
    self.view.addSubview(viewController2.view)

3、ViewController 的界面跳转

  • Objective-C

    	// 跳转到 指定 页面
    [self presentViewController:viewController1 animated:YES completion:nil]; // 返回到 上一个 页面
    [self dismissViewControllerAnimated:YES completion:nil];
  • Swift

    	// 跳转到 指定 页面
    self.presentViewController(viewController1, animated: true, completion: nil) // 返回到 上一个 页面
    self.dismissViewControllerAnimated(true, completion: nil)

4、ViewController 的生命周期

  • 相关方法执行顺序:

    init -> loadView -> viewDidLoad -> viewWillAppear -> viewWillLayoutSubviews -> viewDidLayoutSubviews -> viewDidAppear -> viewWillDisappear -> viewDidDisappear -> viewWillUnload -> viewDidUnload —> dealloc

    分配内存 -> 加载视图 -> 视图已经加载 -> 视图将要出现 -> 将要布局子视图 -> 已经布局子视图 -> 视图已经出现 -> 视图将要消失 -> 视图已经消失 -> 将要销毁视图 (iOS 6+ 已废弃)-> 已经销毁视图(iOS 6+ 已废弃) -> 释放内存

  • viewController 对 view 加载过程:

    • 1、先判断子类是否重写了 loadView,如果有直接调用。之后调 viewDidLoad 完成 View 的加载。
    • 2、如果是外部通过调用 initWithNibName:bundle 指定 nib 文件名的话,ViewController 加载此 nib 来创建 View。
    • 3、如果 initWithNibName:bundle 的 name 参数为 nil,则 ViewController 会通过以下两个步骤找到与其关联的 nib。
      • A、如果类名包含 Controller,例如 ViewController 的类名是 MyViewController,则查找是否存在 MyView.nib;
      • B、找跟 ViewController 类名一样的文件,例如 MyViewController,则查找是否存在 MyViewController.nib。
    • 4、如果子类没有重写的 loadView,则 ViewController 会从 StroyBoard 中找或者调用其默认的 loadView,默认的 loadView 返回一个空白的 UIView 对象。
  • loadView

    	- (void)loadView;
    public func loadView()
    • 加载视图层次结构,用纯代码开发应用程序时使用,功能和 Storyboard & XIB 是等价的,如果重写了 loadView,Storyboard & XIB 都无效。如果控制器的 view 为 nil,会自动调用 loadView 方法,创建视图。
  • awakeFromNib

    	- (void)awakeFromNib;
    public func awakeFromNib()
    • 从 Stroyboard 加载,还没有被设置 frame & bounds。这个方法用的时候,outlet 还没有连接起来,是 ViewController 刚从 storyboard 建的时候没有完全建好,不过可能有一些事情要在这个方法里面完。
  • viewDidLoad

    	- (void)viewDidLoad;
    public func viewDidLoad()
    • 用这个的时候,ViewController 已经完全好了,outlet 也已经连接好了。但是还没有在屏幕上显示出来。这个方法里面可以放很多设置的代码。这个方法执行的时候,view 的 bounds 还没有。先 load,再 appear。

    • 视图加载完成后执行,可以做一些数据初始化的工作,如果用纯代码开发,不要在此方法中设置界面 UI。如果是 Storyboard 开发,可以动态添加一些控件,以及加载数据。

  • viewWillAppear

    	- (void)viewWillAppear:(BOOL)animated;
    public func viewWillAppear(animated: Bool)
    • 这个方法调用的时候,视图的 bounds 已经有了。

    • 视图只会 loaded 一次,但是会 appear 或者 disappear 很多次。不变的东西,放在 viewDidLoad 里面。和几何相关的,放在 viewWillAppear 里面。这点对项目的优化很重要。就好似顶层的 view,旋转 ipad 什么的都需要改变顶层的 view 的大小,当一个 ViewController 的生命周期到这里的时候,就可以在这里的最后时刻来调整 view 的排列或者几何特性。

    • 这里也设置做一些 lazy execution for performance。比如:需要按一个 button,出现一个 view 什么的。这里设置,开销很大。耗时很长的事情最好在 viewWillAppear 里另开一个线程运行,然后在 view 里面放一个小小的 spinning wheel。

  • viewWillLayoutSubviews

       - (void)viewWillLayoutSubviews;
    public func viewWillLayoutSubviews()
    • 这个方法专门用来布局子控件,一般在这里设置子控件的 frame,当控件本身的尺寸发生改变的时候,系统会自动调用这个方法。

    • layoutSubviews 在以下情况下会被调用:

      • 1、init 初始化不会触发 layoutSubviews。
      • 2、addSubview 会触发 layoutSubviews。
      • 3、设置 view 的 Frame 会触发 layoutSubviews,当然前提是 frame 的值设置前后发生了变化。
      • 4、滚动一个 UIScrollView 会触发 layoutSubviews。
      • 5、旋转 Screen 会触发父 UIView 上的 layoutSubviews 事件。
      • 6、改变一个 UIView 大小的时候也会触发父 UIView 上的 layoutSubviews 事件。
  • viewWillDisappear

    	- (void)viewWillDisappear:(BOOL)animated;
    public func viewWillDisappear(animated: Bool)
    • 这个方法在视图将要消失的时候调用。要消失的时候,如果要记得现在的运行情况,如可以记的 scroll 的 position。但是,不要在这个方法里面写太多的东西,否则 App 会崩溃的。另外开线程来处理任何 UI 的改变,或者如果是不怎么废资源的话就直接写入硬盘。
  • Objective-C

    	// 纯代码加载视图
    - (void)loadView { [super loadView]; } // 从 Nib 加载视图
    - (void)awakeFromNib { [super awakeFromNib]; } // 视图已经加载
    - (void)viewDidLoad { [super viewDidLoad]; } // 视图将要出现,在 viewDidLoad 执行完成之后执行
    - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; } // 将要布局子视图
    - (void)viewWillLayoutSubviews { [super viewWillLayoutSubviews]; } // 已经布局子视图
    - (void)viewDidLayoutSubviews { [super viewDidLayoutSubviews]; } // 视图已经出现
    - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; } // 视图将要消失
    - (void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; } // 视图已经消失,不是销毁
    - (void)viewDidDisappear:(BOOL)animated { [super viewDidDisappear:animated]; }
  • Swift

    	// 纯代码加载视图
    override func loadView() { super.loadView() } // 从 Nib 加载视图
    override func awakeFromNib() { super.awakeFromNib() } // 视图已经加载
    override func viewDidLoad() { super.viewDidLoad() } // 视图将要出现,在 viewDidLoad 执行完成之后执行
    override func viewWillAppear(animated: Bool) { super.viewWillAppear(animated) } // 将要布局子视图
    override func viewWillLayoutSubviews() { super.viewWillLayoutSubviews() } // 已经布局子视图
    override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() } // 视图已经出现
    override func viewDidAppear(animated: Bool) { super.viewDidAppear(animated) } // 视图将要消失
    override func viewWillDisappear(animated: Bool) { super.viewWillDisappear(animated) } // 视图已经消失,不是销毁
    override func viewDidDisappear(animated: Bool) { super.viewDidDisappear(animated) }

5、ViewController 更新布局约束

  • 通过代码为 xib 或 storyboard 中 view 增加约束时,尽量避免在 viewDidLoad 中执行,最好放在 updateViewConstraints(UIViewController 中) 或者 updateConstraints(UIView 中)中,记得调用 [super updateViewConstraints] 或者 [super updateConstraints];

  • 如果你真的写在 viewDidLoad 里了,那么可能会遇到这种崩溃错误 Terminating app due to uncaught exception "NSInternalInconsistencyException"。

  • 代码设置约束时,需设置该 view 的 translatesAutoresizingMaskIntoConstraints 属性已设置为 NO。

  • Objective-C

    	// 更新视图控制器中视图的布局约束
    - (void)updateViewConstraints { // 在这里为你的 view 添加约束 [super updateViewConstraints];
    } // 更新视图布局约束
    - (void)updateConstraints { // 在这里为你的 view 添加约束 [super updateConstraints];
    }
  • Swift

    	// 更新视图控制器中视图的布局约束
    override func updateViewConstraints() { // 在这里为你的 view 添加约束 super.updateViewConstraints()
    } // 更新视图布局约束
    override func updateConstraints() { // 在这里为你的 view 添加约束 super. updateConstraints()
    }

6、ViewController 的内存警告

  • iPhone 下每个 app 可用的内存是被限制的,如果一个 App 使用的内存超过 20M,则系统会向该 App 发送 Memory Warning 消息。苹果公司系统工程师建议,应用程序所占内存不应该超过 20MB,开发人员圈内流传着一个粗略的经验法则:当应用程序占用了大约 20MB 内存时,iPhone 开始发出内存警告。收到消息后 App 必须尽可能多的释放一些不必要的内存,当应用程序所占内存大约为 30MB 时,iPhone OS 会关闭应用程序。收到此消息后,App 必须正确处理,否则可能出错或者出现内存泄露。App 收到 Memory Warning 后会调用:UIApplication::didReceiveMemoryWarning -> UIApplicationDelegate::applicationDidReceiveMemoryWarning,然后调用当前所有的 viewController 进行处理。因此处理的主要工作是在 viewController。

  • 内存警告处理思路

    • 通常一个应用程序会包含多个 viewController,当从 view 跳转到另一个 view 时,之前的 view 只是不可见状态,并不会立即被清理掉,而是保存在内存中,以便下一次的快速显现。但是如果应用程序接收到系统发出的 low-memory warning,我们就不得不把当前不可见状态下的 views 清理掉,腾出更多的可使用内存,当前可见的 viewController 也要合理释放掉一些缓存数据,图片资源和一些不是正在使用的资源,以避免应用程序崩溃。具体的实施根据系统版本不同而略有差异。
  • iOS 5 的处理

    • 在 iOS 6 之前,如果应用程序接收到了 low-memory 警告,当前不可见的 viewController 会接收到 viewDidUnload 消息(也可以理解为自动调用 viewDidUnload 方法),所以我们需要在 viewDidUnload 方法中释放掉所有 outlets ,以及可再次创建的资源。当前可见的 viewController 通过 didReceiveMemoryWarning 合理释放资源。

    • 有这样一个 viewController。

      	@interface MyViewController : UIViewController {
      NSArray *dataArray;
      } @property (nonatomic, strong) IBOutlet UITableView *tableView; @end
    • 对应的处理为。

      	- (void)didReceiveMemoryWarning {
      
      		// Releases the view if it doesn't have a superview.
      [super didReceiveMemoryWarning]; // Relinquish ownership any cached data, images, etc that aren't in use.
      } - (void)viewDidUnload { // Relinquish ownership of anything that can be recreated in viewDidLoad or on demand.
      // For example: self.myOutlet = nil;
      self.tableView = nil;
      dataArray = nil; [super viewDidUnload];
      }
  • iOS 6+ 的处理

    • iOS 6 废弃了 viewDidUnload 方法,这就意味着一切需要我们自己在 didReceiveMemoryWarning 中操作。

    • 1、将 outlets 置为 weak。当 view dealloc 时,没有人握着任何一个指向 subviews 的强引用,那么 subviews 实例变量将会自动置空。

      	@property (nonatomic, weak) IBOutlet UITableView *tableView;
    • 2、在 didReceiveMemoryWarning 中将缓存数据置空。不要忘记一点,每当 tableview reload 的时候,需要判断一下 dataArray ,若为空则重新创建。

      	- (void)didReceiveMemoryWarning { 
      
      		[super didReceiveMemoryWarning];
      // Dispose of any resources that can be recreated. dataArray = nil;
      }
  • 兼容 iOS 5 与 iOS 6+

    • 倘若希望程序兼容 iOS 5 与 iOS 6+ 怎么办呢?这里有一个小技巧,我们需要对 didReceiveMemoryWarning 做一些手脚。

      	- (void)didReceiveMemoryWarning {
      
      		[super didReceiveMemoryWarning];
      
      		if (self.isViewLoaded && self.view.window == nil) {
      self.view = nil;
      } dataArray = nil;
      }
      • 判断一下 view 是否是 window 的一部分,如果不是,那么可以放心的将 self.view 置为空,以换取更多可用内存。这样会是什么现象呢?假如,从 viewController A 跳转到 viewController B ,然后模拟 low-memory 警告,此时,viewController A 将会执行 self.view = nil ; 当我们从 B 退回 A 时, A 会重新调用一次 viewDidLoad ,此时数据全部重新创建,

iOS - UIViewController的更多相关文章

  1. iOS UIViewController 和 nib 相关的3个方法

    iOS UIViewController 的 awakeFromNib 以及 - (id)initWithCoder:(NSCoder *)aDecoder 和 - (instancetype)ini ...

  2. iOS UIViewController的瘦身计划

    代码的组织结构,以及为何要这样写. 那些场景适合使用子控制器,那些场景应该避免使用子控制器? 分离UITableView的数据源和UITableViewDataSource协议. MVVM的重点是Vi ...

  3. IOS - UIViewController的生命周期

    1)Load周期 2)Unload周期 在UIViewController中,view(黑体的view指的是controller的view属性)有两个循环:加载和卸载循环.当程序的一部分向contro ...

  4. iOS UIViewController生命周期控制

    具体流程,看下图: init方法在init方法中实例化必要的对象(遵从LazyLoad思想)init方法中初始化ViewController本身 loadView方法当view需要被展示而它却是nil ...

  5. 给iOS开发者的Android开发建议

    本人从事iOS应用开发已经5年有余,直到现在还总是刻意回避Andriod应用的开发.但是不管你信不信,安卓开发还是很有意思的,从iOS转向Android应用开发的跨度并没有你想象的那么大. 现在我把在 ...

  6. iOS开发——UI精选OC篇&UIApplication,UIWindow,UIViewController,UIView(layer)简单介绍

    UIApplication,UIWindow,UIViewController,UIView(layer)简单介绍 一:UIApplication:单例(关于单例后面的文章中会详细介绍,你现在只要知道 ...

  7. iOS阶段学习第30天笔记( UIViewController—Delegate(代理) )

    iOS学习(UI)知识点整理 一.UIViewController的介绍 1)概念:UIViewController 即视图控制器,用来管理和控制页面跳转的一个类 ,iOS里面采用了MVC的体系结构, ...

  8. IOS关于UIViewController之间的切换

    IOS关于UIViewController之间的切换 1.NavigationController切换UIViewController的两种方式 方法一右侧进入 1 SecondViewControl ...

  9. iOS,视图控制器相关(UIViewController)

    1.视图控制器各个方法调用时机 2.选项卡(Tab Bar)和导航栏(Navigation Bar) 3.有无控制器的页面跳转 4.页面跳转隐藏底部选项卡 5.获取导航栏和状态栏高度,隐藏导航栏返回按 ...

随机推荐

  1. 对于改善 MySQL 数据装载操作有效率的方法是怎样

    多时候关心的是优化SELECT 查询,因为它们是最常用的查询,而且确定怎样优化它们并不总是直截了当.相对来说,将数据装入数据库是直截了当的.然而,也存在可用来改善数据装载操作效率的策略,其基本原理如下 ...

  2. PostgreSQL连接python,postgresql在python 连接,创建表,创建表内容,插入操作,选择操作,更新操作,删除操作。

    安装 PostgreSQL可以用Python psycopg2模块集成. sycopg2是Python编程语言的PostgreSQL数据库的适配器. 其程序代码少,速度快,稳定.不需要单独安装这个模块 ...

  3. 每日一九度之 题目1076:N的阶乘

    时间限制:3 秒 内存限制:128 兆 特殊判题:否 提交:7601 解决:2749 题目描述: 输入一个正整数N,输出N的阶乘. 输入: 正整数N(0<=N<=1000) 输出: 输入可 ...

  4. c# ReaderWriterLock类

    先前也知道,Monitor实现的是在读写两种情况的临界区中只可以让一个线程访问,那么如果业务中存在”读取密集型“操作,就 好比数据库一样,读取的操作永远比写入的操作多.针对这种情况,我们使用Monit ...

  5. CentOS 5/6.X 使用 EPEL YUM源

    参考:http://www.linuxidc.com/Linux/2013-08/88523.htm 大纲 一.什么是EPEL? 二.与163 YUM源比较 三.CentOS 5.X 安装使用EPEL ...

  6. tomcat异常: Cannot get a connection, pool exhausted

    1 问题描述Web程序在tomcat刚开始运行时速度很快,但过一段时间后发现速度变得很慢. 检查日志输出,发现异常如下:org.apache.commons.dbcp.SQLNestedExcepti ...

  7. NSIS学习记录の----win8.1和win10对于NSIS创建的卸载快捷方式无法在开始目录下显示

    NSIS提供了很好的软件卸载功能编写的方法,但是针对win8.1和win10操作系统,由于开始目录的权限限制,我们有时候并不能完美的完成所需要的功能----卸载程序的快捷方式不能显示.话不多说,下面提 ...

  8. 【Java】常见的Set类型,HashSet、TreeSet、LinkedHashSet

    HashSet,锋芒毕露,我们最常用到.其他两个,我们较少用到,今天,我们看看他们的区别. import java.util.HashSet; import java.util.Set; public ...

  9. 看懂UML类图和时序图

    看懂UML类图和时序图 这里不会将UML的各种元素都提到,我只想讲讲类图中各个类之间的关系: 能看懂类图中各个类之间的线条.箭头代表什么意思后,也就足够应对 日常的工作和交流: 同时,我们应该能将类图 ...

  10. number_format

    number_format — 以千位分隔符方式格式化一个数字 说明 string number_format ( float $number [, int $decimals = 0 ] ) str ...