复杂 UI && 多视图

参考:

MVC 设计模式

MVC 是一种设计模式。我们创建的对象根据其职责来划分,它要么属于 M:Model,要么属于 V:View,要么属于C:Controller,下面我们分别简要介绍一下 Model、View、ViewController。

Model

Model 一般是我们对真实世界中一些事物的抽象,来定义的一些数据对象,它包含我们所需要的数据以及对这些数据的操作。

Model 一般是负责定义及存储数据,是与用户界面上用于显示的各个视觉元素是无关的。

View

接下来是 View,在 iOS 中,View 一般是 UIKit 中 UIView 及其子类的一些实例,是用户可以看得见的对象。比如说 UIView,是用于界面元素展示的类的基类,以及 UIButton、UILabel、UISlider、UITextField 等等。

ViewController

Objective-C 一般是 ViewController 一般是 UIViewController 及其子类的实例,系统提供给我们的比较常用的 ViewController 有以下的这些:

  • UIViewController:用来展示多个视图控制器;
  • UITabBarController:在 UIViewController 的特性之上,加入可以在这些控制器之间进行切换;
  • UINavigationController:在 UIViewController 的特性之上,在视图控制器间进行导航控制;
  • UITableViewController:展示一个单列的表的视图控制器;
  • UICollectionViewController:以及展示集合视图的;
  • UIAlertViewController:展示弹框消息。

在项目中,ViewController 可以通过持有的方式直接与 View 与 Model 进行通信。

ViewController 的职责是什么?

  1. 视图管理:视图控制器最重要的作用是管理视图的层次结构。

    每个视图控制器都有一个根视图,就是 UIViewControllerview 属性,它包含所有视图控制器的显示内容。在该根视图中,您可以添加显示内容所需的视图。视图控制器始终具有对其根视图的引用,并且根视图视图都具有对其子视图的强引用。

  2. 充当 View 及 Model 的媒介:两者通过 ViewController 进行通信。

  3. 处理用户的交互:通常来说视图将用户的交互事件传递给自己的代理对象来进行事件的处理,这个代理对象通常就是 ViewController。

  4. 资源管理:视图控制器负责管理由其创建的对象。

    在 UIViewController 子类中,是我们开发者负责管理显式创建的任何对象。由于移动设备的内存是有限的,当设备的可用内存不足时,UIKit 会要求应用程序释放他们不再需要的任何资源。

    • 这样做的一种方法是调用视图控制器的 didReceiveMemoryWarning 方法:在这个方法里删除对不再需要的对象,或者稍后可以轻松地重新创建的对象的引用。

    • 例如,您可以使用该方法删除缓存的数据。在发生内存不足的情况时,尽可能多地释放内存是非常重要。消耗过多内存的应用程序可能会被系统彻底终止以恢复内存。

OC 中的内容 VC 种类

UIScrollView

这是一个比较重要的类,UITableView, UICollectionView, UITextView 这些视图都是继承自它。在 iOS 中,滚动视图 UIScrollView 通常用于查看大于屏幕的内容。

两个比较重要的属性:

  • frame:这个属性控制了 UIScrollView 的位置以及大小;可以理解为窗口的大小;
  • ContentSize:定义了 UIScrollView 能显示的内容区域的大小;可以理解为图片的大小;

很显然,如果将 frame 的宽度或者高度设置为最大,就可以实现拖动和分页了。但是如何让它恰好停止在下一个视图呢,只需要设置下面的属性:

1
scrollView.pagingEnable = YES;

UITableView

UIScrollView 的子类,可以通过 initWithFrame 创建:

1
2
3
- (instancetype)initWithFrame:(CGRect)frame style:(UITableViewStyle)style NS_DESIGNATED_INITIALIZER;
// must specify style at creation.
// - initWithFrame: calls this with UITableViewStylePlain

在创建时必须指定一个创建风格,有以下两个选项:

  • UITableViewStylePlain:通讯录类似风格;
  • UITableViewStyleGrouped:系统设置类似风格。

UITableView 主要是以下的几个部分组成:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
+---------------+            >+-----------------------+ 
|               |          -/ |                       | 
|  header view  |         /   |  section header view  | 
|               |       -/    |                       | 
-----------------      /      ------------------------- 
|  section 1    |     /       |  cell 1               | 
-----------------   -/        ------------------------- 
|  ...          |  /          |  ...                  | 
|  ...          |-/           |  ...                  | 
----------------/             ------------------------- 
|  section 2    |             |  cell 2               | 
-----------------\            ------------------------- 
|  section 3    | -\          |  cell 3               | 
-----------------   -\        ------------------------- 
|               |     -\      |                       | 
|  footer view  |       -\    |  section footer view  | 
|               |         -\  |                       | 
+---------------+           ->+-----------------------+ 

如何创建 UITableView 的子视图?

  • 对于 UITableView,我们一般不通过创建子视图并添加其为 UITableView 的子视图来展示内容;

  • 一般把这个工作交给一个遵守 UITableViewDataSource 协议的对象:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    @optional
    - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView;
    // 用于指定一个 TableView 中有多少个 Section。可选方法,默认返回为 1
    
    - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section;
    // 这个方法告诉 UITableView 第几个 section 要显示多少行内容
    
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
    // 在指定的 indexPath 上具体的要展示的 Cell 长什么样子
    
  • PostScript:上面的第三个方法,引入了 NSIndexPath 这个类型,他用于指定一个 Cell 的位置。我们可以使用以下的类方法直接创建这个对象:

    1
    2
    
    + (instancetype)indexPathForRow:(NSInteger)row inSection:(NSInteger)section;
    + (instancetype)indexPathForItem:(NSInteger)item inSection:(NSInteger)section NS_AVAILABLE_IOS(6_0);
    

UITableViewCell

上面 UITableViewDataSource 协议的第三个方法指定了 UITableViewCell 这个对象,顾名思义,他就是用来指定 cell 样式的一个类。

这个对象有一个名为 contentView 的子视图,它有三个子视图。关于这三种视图有以下的四种排列模式:

../TableViewCellMode.png

OC 中的容器 VC 种类

UINavigationController

ViewController 的栈操作包括 Pushing、Poping,其他的展现方式还包括 Presenting、Dissmissing;

注意以下的几点:

  1. 所有通过 prensent 方式展现的 ViewController 都需要主动调用 dismiss

  2. 使用 push 方法展现 ViewController 需要通过调用 pop 方法进行消失展示;

  3. push 以及 pop 方法只可以在 UINavigationController 上被调用(其他的只能调用 present);

  4. 当调用一个 ViewControllerdismiss 方法时,所有通过该 vc present 出来的 vc 都会被销毁,同时只有最顶部的 ViewController 会展现动画。

    swift 为 UIViewController 定义了一个属性 presentingViewController.dissmiss 用于执行这一操作;

  5. 同时 Apple 也提供了一个 API,用于仅仅销毁当前 vc,presentedViewController.dismiss

presentedViewControllerpresentingViewController

  • UIViewController 的两个属性,默认值为 nil
  • 当 vcA 通过 presentViewController:animated:completion: 方法展现 vcB 时:
    • vcB.presentingViewController 会被置为 vcA
    • vcA.presentedViewController 会被置为 vcB

UITabBarController

iOS 官方 App 中“博客”、“闹钟”等的底部导航风格。