iOS开发之常用控制器及多控制器管理

远书归梦两悠悠,只有空床敌素秋。
阶下青苔与红树,雨中寥落月中愁。

李商隐 《端居》

控制器

控制器View的加载

hack_appList_result_failed

控制器的生命周期

hack_appList_result_failed

内存警告的时候的整个循环的流程图

hack_appList_result_failed

控制器常见的创建方式有以下几种

通过storyboard创建

  • 先加载storyboard文件(Test是storyboard的文件名)
1
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Test" bundle:nil];
  • 接着初始化storyboard中的控制器
  • 初始化“初始控制器”(箭头所指的控制器)
1
XSViewController *vc = [storyboard instantiateInitialViewController];
1
2
 // 通过一个标识初始化对应的控制器
XSViewController *XS = [storyboard instantiateViewControllerWithIdentifier:@”vc"];

直接创建

1
XSViewController *XS = [[XSViewController alloc] init];

指定xib文件来创建

1
2
3
4
5
6
7
8
9
10
11
12
 // 通过Xib创建的控制器

// 通过xib去加载控制器
// 1.首先创建一个Xib文件
// 2.Xib文件需要拖一个View描述控制器的View
// 3.需要把Xib上的View与控制器连线,设置Xib的File'owner为控制器
ViewController *vc = [[ViewController alloc] initWithNibName:@"VC" bundle:nil];

// 创建根控制器
XSViewController *vc = [[XSViewController alloc] init];

// init底层会自动调用initWithNibName
  • 假设通过Xib创建XSViewController控制器对象
    • 如果nibName为nil,首先会去查找XSView.xib,如果有,就会去加载XSView.xib描述的控制器的view
    • 如果没有找到,就会去找XSViewController.xib,如果有,也会去加载
    • 都没有找到,就会生成一个空的view
      • 空的view是几乎透明的,但不是完全透明的
      • 相当于[UIColor clearColor];可以穿透过去,但是完全透明就不能够穿透
  • 控制器的view是延迟加载的:用到时再加载UIView(只用于UIViewController或者子控制器)
    • 可以用isViewLoaded方法判断一个UIViewController的view是否已经被加载
    • 控制器的view加载完毕就会调用viewDidLoad方法

UINavigationController

  • 为了便于管理控制器,iOS提供了2个比较特殊的控制器
1
2
UINavigationController(导航控制器)
UITabBarController(QQ,微信,淘宝等)

UINavigationController的使用步骤

  • 初始化UINavigationController
  • 设置UIWindow的rootViewController为UINavigationController
  • 根据具体情况,通过push方法添加对应个数的子控制器

UINavigationController的子控制器

  • UINavigationController 以的形式保存子控制器
1
2
@property(nonatomic,copy) NSArray *viewControllers;
@property(nonatomic,readonly) NSArray *childViewControllers;

控制器出入栈的常用的集中方法

1
2
3
4
5
6
7
8
// 使用push方法能将某个控制器压入栈
- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated;
// 使用pop方法可以移除控制器 将栈顶的控制器移除
- (UIViewController *)popViewControllerAnimated:(BOOL)animated;
// 回到指定的子控制器
- (NSArray *)popToViewController:(UIViewController *)viewController animated:(BOOL)animated;
// 回到根控制器(栈底控制器)
- (NSArray *)popToRootViewControllerAnimated:(BOOL)animated;

如何修改导航栏的内容

  • 显示在我们眼前的永远是栈订控制器
  • 导航栏的内容由栈顶控制器的navigationItem属性决定
  • 导航控制器是以栈的形式先进后出后进先出(是以数组的形式存储的 数组就是以栈的形式存储的)
  • 导航栏返回按钮的文字是由上一个控制器决定的(特例)

UINavigationItem有以下属性影响着导航栏的内容

1
2
3
4
5
6
7
8
9
10
11
// 左上角的返回按钮
@property(nonatomic,retain) UIBarButtonItem *backBarButtonItem;
// 中间的标题视图
@property(nonatomic,retain) UIView *titleView;
// 中间的标题文字
@property(nonatomic,copy) NSString *title;
// 左上角的视图
@property(nonatomic,retain) UIBarButtonItem *leftBarButtonItem;
// 右上角的视图
UIBarButtonItem *rightBarButtonItem
@property(nonatomic,retain) UIBarButtonItem *rightBarButtonItem;

Segue的属性

  • 每一个Segue对象,都有3个属性
1
2
3
4
5
6
// 唯一标识
@property (nonatomic, readonly) NSString *identifier;
// 来源控制器
@property (nonatomic, readonly) id sourceViewController;
// 目标控制器
@property (nonatomic, readonly) id destinationViewController;
hack_appList_result_failed

hack_appList_result_failed

Segue的类型

  • 自动型:点击某个控件后(比如按钮),自动执行Segue,自动完成界面跳转
  • 手动型:需要通过写代码手动执行Segue,才能完成界面跳转

设置导航控制器的导航栏的主题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/** 只会在系统第一次用这类的时候调用(只会调用一次)
*/
+ (void)initialize{

// 1.设置导航栏主题样式
UINavigationBar *navBar = [UINavigationBar appearance];

// 1.1.设置导航栏背景图片
NSString *navImageName = nil;
if (ios7) {
navImageName = @"NavBar64";
navBar.tintColor = [UIColor whiteColor]; // 设置导航栏的返回按钮颜色为白色
}else{ // ios6
navImageName = @"NavBar";
}
[navBar setBackgroundImage:[UIImage imageNamed:navImageName] forBarMetrics:UIBarMetricsDefault];

// 1.2.设置导航栏的字体颜色为白色
NSDictionary *attrButes = @{
NSForegroundColorAttributeName : [UIColor whiteColor],
NSFontAttributeName : [UIFont systemFontOfSize:18]
};
[navBar setTitleTextAttributes:attrButes];
}

注意点

  • 以后看到系统自带的类带有item,就是苹果提供的模型
    • UINavigationItem : 设置导航条内容(她不是控件 是模型)
    • UIBarButtonItem : 设置导航条上按钮的内容 (模型)

UITabBarController

  • 跟UINavigationController类似,UITabBarController也可以轻松地管理多个控制器,轻松完成控制器之间的切换,典型例子就是QQ、微信等应用.
  • UITabBarController的使用步骤
    • 初始化UITabBarController
    • 设置UIWindow的rootViewController为UITabBarController
    • 根据具体情况,通过addChildViewController方法添加对应个数的子控制器

UITabBarController添加控制器的方式有2种

1
2
3
4
5
- 添加单个子控制器
- (void)addChildViewController:(UIViewController *)childController;

- 设置子控制器数组
@property(nonatomic,copy) NSArray *viewControllers;

UITabBarController注意点

  • 如果UITabBarController有N个子控制器,那么UITabBar内部就会有N个UITabBarButton作为子控件
  • UITabBarButton里面显示什么内容,由对应子控制器的tabBarItem属性决定
    hack_appList_result_failed

多控制器管理

segue跳转之前会调用的方法

hack_appList_result_failed
  • 除了push之外,还有另外一种控制器的切换方式,那就是Modal (能用push的尽量用push实现,公司有特殊需求可以用modal)
  • 任何控制器都能通过Modal的形式展示出来
  • Modal的默认效果:新控制器从屏幕的最底部往上钻,直到盖住之前的控制器为止
1
2
3
4
5
// 以Modal的形式展示控制器
- (void)presentViewController:(UIViewController *)viewControllerToPresent animated: (BOOL)flag completion:(void (^)(void))completion

// 关闭当初Modal出来的控制器
- (void)dismissViewControllerAnimated: (BOOL)flag completion: (void (^)(void))completion;

规律

  • modal出谁,谁就能dismiss
  • 谁modal,谁也可以dismiss
  • dismissViewController 永远移除最后显示的控制器

注意

  • 如果一个控制器的View显示到屏幕上,这个控制器一定不能被销毁(要被强引用).
  • 谁modal谁就强引用modal出来的控制器

多控制器 管理

UIWindow

  • 主窗口的概念
  • 新建UIWindow

    UIViewController

  • 控制器的创建方式
  • 控制器view的创建方式
  • view的懒加载
1
loadView、viewDidLoad、viewDidUnload、didReceiveMemoryWarning

UINavigationController

  • 通过非storyboard方式
    • 创建导航控制器
    • 设置UIWindow的根控制器
    • push 1个、2个、3个 子控制器
    • 解释push的原理(栈、导航控制器的管理过程)
    • 栈底、栈顶控制器的概念
    • 如何设置导航栏上面的内容、返回文字的设置
    • pop的方法使用
    • push和addChild、viewControllers和childViewController的关系
  • 通过storyboard方式

多控制器的数据的传递方式(两种):顺传和逆传

  • 顺传

    • 控制器的跳转方向: A C
    • 数据的传递方向 : A C
    • 数据的传递方式 :
    • 在A的prepareForSegue:sender:方法中根据segue参数取得destinationViewController, 也就是控制器C,直接给控制器C传递数据(要在C的viewDidLoad方法中取得数据,来赋值给界面上的UI控件)
  • 逆传

    • 控制器的跳转方向: A C
    • 数据的传递方向 : C A
    • 数据的传递方式:
    • 让A成为C的代理,在C中调用A的代理方法,通过代理方法的参数传递数据给A

App主流UI框架结构

hack_appList_result_failed
要不要鼓励一下😘