UIViewController

1.UIViewController

UIViewController官方文档

1.1 定义

用来管理UIKit应用程序的视图层次结构的对象。

1.2 声明

1
class UIViewController : UIResponder

1.3 概述

UIViewController类定义了所有视图控制器共同的共享行为。

不需要创建它的实例,直接子类化UIViewController,然后添加需要的方法和属性即可。

一个视图控制器的主要职责包括:

  1. 根据基本数据的更改来更新视图内容。
  2. 响应用户与视图的交互。
  3. 调整视图大小,并管理整个界面的布局。
  4. 与app中的其他对象(包括其他的视图控制器)进行协调

一个视图控制器和他管理的视图是紧密绑定的,也参与了他的视图层次结构的事件处理。

具体说来,视图控制器是UIResponder对象,并插入到该视图控制器的根视图和其父视图(通常属于其他的视图控制器)直接的响应链中。

若一个视图控制器的所有视图都没处理某事件,那么该视图控制器可选择处理该事件或者将其传给父视图。

视图控制器很少单独使用,我们经常使用多个视图控制器,每个视图控制器都能拥有app用户界面的一部分。
例如:一个视图控制器用于显示一个项目表,而另一个视图控制器则显示从该表中选择的项目。

通常一次只能看到一个视图控制器中的视图

一个视图控制器可以代表另一个视图控制器来显示新的视图集,或者可以充当其他视图控制器内容的容器并根据需要设置视图。

1.4 子类说明

每个app都至少包含一个UIViewController的自定义子类。通常都会包含多个自定义的视图控制器。

自定义的视图控制器定义了app的整体行为,包括app的外观以及如何响应用户的交互。

有关使用和实现视图控制器的详细信息,请参见《适用于iOS的视图控制器编程指南》

1.5 自定义的视图控制器执行的任务

1.5.1 视图管理

每个视图控制器管理一个视图层次结构,其根视图存储在该类的 view属性中

根视图主要充当其余视图层次结构的容器。根视图的大小和位置取决于拥有它的对象,该对象可以是父视图控制器,也可以是app的窗口。

app窗口拥有的视图控制器是app的根视图控制器,其视图大小也设为填充窗口。

视图控制器延迟加载其视图,首次访问视图的属性会加载或创建视图控制器的视图。

有几种方法可以为视图控制器指定视图:

  1. 在app的Storyboard中指定视图控制器和他的视图。

    情节提要是指定视图的首选方法。通过情节提要,不仅可以指定视图与视图控制器的连接,还可以指定视图控制器之间的关系和顺序,使得查看和修改app的行为更加容易。

    调用相关UIStoryboard对象的instantiateViewController(withIdentifier:) 方法来从情节提要中加载视图控制器。

  2. 使用Nib file来为视图控制器指定视图。

    nib文件可以指定一个视图控制器的视图,但是不能指定视图之间的关系和顺序。

    nib文件仅存储有关视图控制器本身的最少信息。

    先手动创建视图控制器类,然后调用init(nibName:bundle:)方法对齐初始化。

  3. 使用loadView()方法为视图控制器指定视图。

这三种方法的结果都一样,都是创建适当的视图集,然后通过view 属性将其公开。

重要

视图控制器是其视图及其创建的视图及子视图的唯一所有者。它负责创建这些视图,并在适当的时间(例如,释放视图控制器本身时)放弃对它们的所有权。

如果使用情节提要或Nib文件存储视图对象,则当视图控制器要求提供这些视图时,每个视图控制器对象都会自动获得其视图的副本

但是,如果您手动创建视图,则每个视图控制器必须具有其自己的唯一视图集。您不能在视图控制器之间共享视图。

视图控制器的根视图的大小始终适合其分配的空间。

对于其他的视图可以使用Interface Builder来指定Auto Layout约束,这些约束控制每个视图在其超级视图范围内的位置和大小。

也可以手动创建约束,当需要的时候可以将其加入你的视图中。

有关如何创建约束的更多信息,请参见 Auto Layout Guide

1.5.2 处理与视图相关的通知

当其视图的可见性更改时,视图控制器会自动调用其自己的方法,以便子类可以响应此更改。

使用 viewWillAppear(_:)为视图准备好显示在屏幕,使用viewWillDisappear(_:)来保存更改或者其他状态信息。

下图展示了一个视图控制器的视图的可视化状态以及可能发生的状态转换:

img

并非所有的“ will”回调方法都只与一个“ did”回调方法配对。您需要确保,如果使用“ will”回调方法启动了一个进程,则将以相应的“ did”相反的“ will”回调方法结束该进程。

1.5.3 处理视图旋转

从iOS 8开始,不推荐使用所有与旋转相关的方法。取而代之的是,旋转使用 viewWillTransition(to:with:)方法来改变视图控制器中视图的大小。当界面方向改变时,UIKit在窗口的根视图控制器上调用该方法。然后该视图控制器会在视图控制器层次结构中传播,通知他的子视图控制器。

在IOS 6和7中,app支持在app的Info.plist文件中定义界面方向。一个视图控制器可以重写supportedInterfaceOrientations方法来app所限制支持的方向。通常来说,系统会在根视图控制器中或者视图填充整个屏幕的视图控制器中调用这个方法;子视图控制器只能使用父视图控制器为其提供的窗口部分,不能直接参与支持哪些旋转的决策。应用程序的orientation mask和视图控制器的orientation mask的交集用于确定视图控制器可以旋转到哪些方向。

您可以为一个以特定方向全屏显示的视图控制器重写 preferredInterfaceOrientationForPresentation方法。

当一个可见的视图控制器发生旋转的时候,在旋转过程中会调用 willRotate(to:duration:), willAnimateRotation(to:duration:), 和 didRotate(from:) 方法。

在该视图被其父视图调整大小并定位之后会调用 viewWillLayoutSubviews()方法。

当一个视图控制器方向变化时,如果其不可见,则不会调用旋转方法。但是当其变为可见时会调用 viewWillLayoutSubviews() 。你可以调用statusBarOrientation实现上述方法来决定设备方向。

注意

在启动时,app应始终以纵向方向设置其界面。在application(_:didFinishLaunchingWithOptions:)方法返回之后,app使用视图控制器旋转机制对视图旋转到适当的方向以显示界面。

1.5.4 实现容器视图控制器

自定义UIViewController子类也可以充当视图控制器容器。视图控制器容器管理着其子视图控制器的内容表示。子视图可以按原样显示,也可以与容器视图控制器容器拥有的视图结合显示。

视图控制器容器子类应该声明一个公共接口来关联其子级。这些方法的性质取决于你和你创建的容器的语义。你要决定你的视图控制器一次能显示多少个子级、什么时候显示、在你的视图控制器的视图层次结构中的显示位置。你的视图控制器定义了与子级的哪些关系。通过为容器建立一个干净的公共接口,可以确保子级在逻辑上使用其功能,而不必访问太多有关容器如何实现该行为的私有详细信息。

您的容器视图控制器必须在将子视图的根视图添加到视图层次结构之前,将子视图控制器与其自身关联。这样,iOS可以将事件正确地路由到子视图控制器以及这些控制器管理的视图。同样,在从其子视图层次结构中删除子视图的根视图之后,也应将该子视图控制器与其自身断开连接。为了建立或破坏这些关联,您的容器调用由基类定义的特定方法。容器类的客户端不会调用这些方法,只能由您的容器的实现用于提供预期的容纳行为来调用。

可能调用的重要方法:

注意

创建容器视图控制器时,不需要重写任何方法。

默认情况下,旋转和界面回调会自动转发给子级。您可以选择重写 shouldAutomaticallyForwardRotationMethods()shouldAutomaticallyForwardAppearanceMethods 方法来自己控制此行为。

1.5.5 内存管理

内存是iOS中的重要资源,视图控制器提供了内置支持,可在关键时刻减少其内存占用。UIViewController类通过 didReceiveMemoryWarning()提供的低内存环境下的一些自动处理方法,该方法会释放不需要的内存。

1.5.6 状态保存与还原

如果给视图控制器的restorationIdentifier属性赋值的话,在app转到后台时系统可能要求视图控制器对自身进行编码。保存时,视图控制器将保留其视图层次结构中也具有还原标识符的所有视图的状态。视图控制器不会自动保存任何其他状态。如果要实现自定义容器视图控制器,则必须自己对所有子视图控制器进行编码。您编码的每个子代都必须具有唯一的还原标识符。

有关系统如何确定要保留和还原哪些视图控制器的更多信息,请参阅App Programming Guide for iOS

要查看状态保存和还原的示例,请参阅Restoring Your App’s State

2.UITabBarController

UITabBarController

2.1 定义

是一个container view controller,用来管理radio类型的选择界面,决定显示哪个child view controller

2.2 声明

1
class UITabBarController: UIViewController

2.3 概述

tab bar界面在窗口底部显示tabs,用来选择不同的模式来为每个模式展示界面。这个class可以直接按原样使用,也可以子类化。

tab bar controller中的每个tab都和一个自定义的view controller相关联。当用户选择一个tab时,tab bar controller就会显示对应的view controller 的根视图来代替之前的视图。(用户点击的时候显示的总会是根视图)因为选了一个tab来代替界面的内容,每个tab管理的界面类型不需要相似。事实上,tab bar界面常用来表示不同类型的信息或者用不用类型的界面显示相同信息。下图展示了闹钟界面提供的tab bar界面

Four screenshots of the Clock app, showing the World Clock, Alarm, Stopwatch, and Timer tabs

永远不要直接访问tab bar controller的视图,可以将包含根视图的view controller赋值给每个tab的viewControllers属性。

指定view controller的顺序决定了他们在tab bar中展示的顺序。

在赋值给上面那个属性的时候,也要同时赋值给selectedViewController属性,来表明初始显示哪个view controller。(也可以通过selectedIndex属性来使用数组的index指定view controller)

当你把tab bar controller的视图(通过继承的view属性获取)嵌入window的时候,tab bar controller会自动选择view controller并显示其内容、重设其大小以适应tab bar界面。

tab bar的items是通过他们相关联的view controller来设置的。

要关联一个tab bar item和一个view controller,可以先创建一个UITabBarItem类的实例,配置好之后赋值给view controller的tabBarItem属性。

如果不为view controller提供一个自定义的tab bar item,view controller会创建一个没有图片、文本为view controller 的title属性的默认的item。

当用户和tab bar界面交互时,tab bar controller对象会发送交互通知给他的delegate。

delete可以是遵循UITabBarControllerDelegate协议的任意对象。

可以使用delegate使得特定的item不被选择,或者在选择之后做些额外工作。

可以使用delegate监听More navigation controller对tab bar的更改,参考The More Navigation Controller

2.4 Tab Bar Controller的视图

  • 由于tab bar controller继承自UIViewController,所以他有自己的视图,可以通过view获取到。
  • tab bar controller的视图只是一个tab bar view(放置自定义的内容)的容器,tab bar view 为用户提供了选择控制,并且包含多个tab bar item。
  • 尽管tab bar和toolbar中的items改变,但是管理他们的views不会变。只有自定义的content view会改变以反映当前选择的tab的view controller。
  • 可以选择navigation controller或者自定义的视图控制器作为一个tab的根视图控制器。
  • 若根视图控制器是navigation controller,tab bar controller会进一步调整导航内容的尺寸,防止它覆盖tab bar。
  • 在tab bar 界面显示的视图都要设置autoresizingMask 来调整视图以适应任何情况。

2.5 More Navigation Controller

  • tab bar只有有限的空间来显示自定义的items。如果添加了6个或者更多的视图控制器到tab bar controller中,tab bar controller只会显示前四个,然后把More添加到tab bar。点击More会弹出一个界面来选择剩余的items。
  • 标准的More界面包括编辑按钮,以便于允许用户来对tab bar重新编辑。
  • 默认情况下,用户可以对tab bar上的所有items进行重排。如果不想让用户进行重排,可以从customizableViewControllers的数组中移除对应的视图控制器。

2.6 状态保存

  • 在iOS6及更新版本,如果给视图控制器的restorationIdentifier属性进行赋值,他会在选中的tab中保留一个对这个视图控制器的引用。在恢复时,就会使用这个引用来选中tab中的这个视图控制器。
  • 在保存tab bar controller时,把一个唯一的恢复标识符赋值给你要保存的子视图控制器。如果省略恢复标识符的话,tab会返回默认的配置。
  • 尽管tab bar controller保存的tab的顺序和viewControllers中的一样,但是保存顺序实际上是不相关的。
  • 在代码中应该在下次启动中提供新的tab bar controller,所以可以根据需要控制tab的顺序。
  • 状态恢复系统是根据恢复标识符来恢复每个tab的内容的,而不是根据他们的位置。

更多关于状态保存和恢复的信息可以参考App Programming Guide for iOS

2.7 iOS和tvOS之间的区别

3.UINavigationController

基于栈(有序数组)的视图控制器容器

3.1 声明

1
class UINavigationController : UIViewController

3.2 概述

  • navigation controller是个在导航层次上管理多个字视图控制器的视图控制器容器。
  • 一次只有一个子视图控制器是可见的,放入一个新的视图控制器后会隐藏之前的视图控制器。
  • 点击返回按钮,会栈顶的视图控制器移除,显示下面的视图控制器
  • 使用导航界面来模拟app管理的层次结构,在层次结构的每一级要提供一个恰当的界面(被自定义的视图控制器所管理)来显示内容。
  • 除了根视图控制器,其他都有返回按钮,栈底的就是根视图控制器。
  • 可以使用类方法或者返回按钮移除栈顶的视图控制器。
  • navigation controller管理着界面顶部的UINavigationBar,和底部的optional toolbar。
  • navigation bar一直存在,被导航控制器自身所管理,使用子视图控制器提供的内容来更新。
  • isToolbarHidden 属性为false时,导航控制器同样会使用栈顶的视图控制器的内容来更新toolbar。
  • navigation controller使用delegate 对象可以重写放入或者弹出的视图控制器,提供自定义的动画过渡,指定导航界面的首选方向。
  • delegate对象必须遵循UINavigationControllerDelegate 协议

img

3.3 navigation controller的视图

  • 内嵌了其他视图控制器的内容,通过view 属性可以获取他的视图。这个视图合并了navigation bar、optional toolbar、和栈顶的视图控制器的content view。

The views of a navigation controller

  • 在iOS7及更新的版本中, content view和navigation bar有重叠,所以在设计视图控制器内容的时候要考虑到这一空间。
  • navigation controller控制着navigation bar和tool bar到创建、配置、和显示。你可以自定义navigation bar的外观属性,但是禁止直接改变frame, bounds, 或者 alpha 的值。
  • 如果要对UINavigationBar子类化,一定要使用init(navigationBarClass:toolbarClass:) 方法来进行初始化。
  • 要隐藏或者显示navigation bar,可以使用isNavigationBarHidden 属性或者 setNavigationBarHidden(_:animated:)方法。
  • navigation controller会使用导航栈中的视图控制器相对应的UINavigationItem对象,来动态生成navigation bar的内容。
  • 要想自定义navigation bar的整体外观,请使用UIAppearance 的API。
  • 要改变navigation bar的内容,要配置自定义的视图控制器的navigation items。

3.4 更新Navigation Bar

  • 每次顶层的视图控制器改变,都会直接更新navigation bar。尤其是,navigation controller会更新navigation bar 的左边、中间、右边的UIBarButtonItem 。可以使用自定义的内容或者系统内置的内容来创建items。
  • 使用tintColor属性可以改变bar中的item的颜色,使用barTintColor可以改变bar本身的颜色。navigation bar不会继承当前显示的视图控制器的着色

a)The left item

除了根视图控制器以外,navigation bar左边的item都是返回到之前到视图控制器。左侧按钮到内容由以下决定:

  1. 如果顶层的视图控制器有自定义的左侧按钮,就会显示它。使用该视图控制器的navigation item的leftBarButtonItem属性来自定义。
  2. 如果顶层的视图控制器没有自定义的左侧按钮,但是前一个视图控制器的navigation item的backBarButtonItem属性有个对象,就会显示它。
  3. 如果这两个视图控制器都没有自定义bar button,就会使用默认的返回按钮,按钮上的文字使用前一个视图控制器的title属性,但是若文字过长则会使用“Back”代替。

b)The Middle Item

  • 若顶层的视图控制器的navigation item有自定义的titleView ,就会使用它代替默认的title view。
  • 如果没有自定义的title view,就会显示视图控制器的title属性。也可手动设置视图控制器的navigation item的title属性来显示。

c)The Right Item

  • 若顶层视图控制器的navigation item有自定义的rightBarButtonItem ,就会显示它。
  • 若没有,则不显示。

3.5 显示ToolBar

  • navigation controller 对象中有一个可选的toolbar,当显示时,会显示激活的视图控制器的toolbarItems属性。当激活的视图控制器改变时,toolbar会自动改变显示新的item。
  • toolbar默认隐藏的。通过setToolbarHidden(_:animated:) 可以使其显示。如果不是所有的视图控制器都显示toolbar,代理对象在视图控制器push和pop时会自动调用该函数,来显示或者隐藏toolbar。
  • 要想使用自定义的UIToolbar,可以使用init(navigationBarClass:toolbarClass:) 方法来初始化navigation controller。

⚠️:若使用自定义的toolbar和navigation bar来创建navigation controller,在将navigation controller显示在屏幕上之前,你需要负责push和set视图控制器。

3.6 适应不同的环境

  • 导航界面在horizontally compact和horizontally regular环境下是一样的。在他们之间切换时,只有视图控制器中的视图大小改变。navigation controller的视图层次结构和布局中的视图不变。
  • 在配置导航栈中的视图控制器的segues时,标准的Show和Show Detail segues行为如下:
    • Show segues – 将一个视图控制器放入导航栈
    • Show Detail segues – navigation controller以modally形式展示一个视图控制器。

3.7 界面行为

  • 界面方向 – 当确定界面方向时,导航控制器并不参考导航栈中的视图控制器。在iPhone中。导航控制器支持除纵向颠倒以外的所有方向。在iPad中,导航控制器支持所有方向。若导航控制器有个代理对象,那么代理对象可以使用navigationControllerSupportedInterfaceOrientations(_:) 指定他所支持的方向。
  • 演示上下文 – 导航控制器在模态展示视图控制器时可以定义展示的上下文。当模态转换类型是UIModalPresentationStyle.currentContext 或者 UIModalPresentationStyle.overCurrentContext时,导航栈中的视图控制器的模态演示覆盖了整个导航界面。

3.8 状态保存

  • 当把一个值赋值给导航控制器的restorationIdentifier 属性时,会尝试保存自身和导航栈中的子视图控制器。
  • 导航控制器从栈底开始,向上对每个有有效恢复标识字符串对视图控制器进行编码。
  • 下个启动周期过程中,导航控制器会将保存的视图控制器恢复到导航栈中(顺序和保存前一样)。
  • 放入导航栈中的子视图控制器可以使用相同的恢复标识符。导航控制器会自动添加额外的信息来保证每个子视图控制器的保存路径都不相同。
  • 如何保存和恢复状态,可参考Preserving Your App’s UI Across Launches
0%