详解 View Controller

2,094 阅读3分钟

类UIViewController,它被称为视图控制器,被用来装入和释放视图、管理视图交互、并且和其他视图控制器一起协作完成整体的App界面。为了术语一致,后文会直接使用它的英文名:View Controller。

View Controller管理一个视图层级体系。比如在一个视图控制器内加入一个按钮视图和一个标签视图,分别名为button、label,那么层次系统如下:

    ViewController:UIViewController
        view:UIView
            button:UIButton
            label:UILabel

本文引入一个微小的,但是可以和用户交互的案例,来演示View Controller的视图管理能力。基于前文提到的脚手架,代码如下:

  1. 界面包括一个按钮和一个标签,标签初始值为0
  2. 当点击按钮时,标签的数字会被累加1

代码如下:

    import UIKit
    @UIApplicationMain
    class AppDelegate: UIResponder, UIApplicationDelegate {
        var window: UIWindow?
        func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
            self.window = UIWindow(frame: UIScreen.main.bounds)
            let page = Page1()
            self.window!.rootViewController = page
            self.window?.makeKeyAndVisible()
            return true
        }
    }
    class Page1: UIViewController {
        var count = 0
        var label : UILabel!
        override func viewDidLoad() {
            super.viewDidLoad()
            self.view.backgroundColor = .white
            label   = UILabel()
            label.frame = CGRect(x: 100, y: 100, width: 20, height: 50)
            label.text =  "0"
            view.addSubview(label)
            let button   = UIButton(type: .system)
            button.frame = CGRect(x: 120, y: 100, width: 20, height: 50)
            button.setTitle("+",for: .normal)
            button.addTarget(self, action: #selector(Page1.buttonAction(_:)), for: .touchUpInside)
            view.addSubview(button)
        }
        func buttonAction(_ sender:UIButton!){
            self.count +=  1
            label.text =  self.count.description
        }
    }

编译运行后会看到界面上的按钮和标签,点击按钮标签的值加1,说明App满足我们的最初需求。

代码解释下:

  1. UIViewController类内属性view可以把其他view加入其内
  2. 按钮的类为UIButton ,可以通过属性frame设置位置和大小,可以通过UIViewController.view对象的方法addSubview把按钮加入到UIViewController内
  3. 标签的类为UILabel,可以通过属性frame设置位置和大小,可以通过UIViewController.view对象的方法addSubview把按钮加入到UIViewController内
  4. button可以添加事件,通过方法:

       button.addTarget(self, action: #selector(Page1.buttonAction(_:)), for: UIControlEvents.touchUpInside)

View Controller内部根据管理的对象的不同,分为两种:

  1. Content View Controller ,内容型
  2. Container View Controller,容器型

两者的不同之处在于:Container View Controller不但可以管理层次化视图,还可以管理其他的View Controller,从中取得部分视图来嵌入自己的整体界面。Container View Controller需要设置嵌入视图的位置和大小,而原本所在的View Controller管理视图的内容和事件。

Container View Controller存在的目的是为了更好的做View Controller之间的导航,比如UINavigationController, UITabBarController就是典型的Container View Controller。

如下案例演示一个View Controller作为Container View Controller嵌入另外一个View Controller到自己的用户界面内:

    import UIKit
    @UIApplicationMain
    class AppDelegate: UIResponder, UIApplicationDelegate {
        var window: UIWindow?
        func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
            self.window = UIWindow(frame: UIScreen.main.bounds)
            self.window!.rootViewController = Page1()
            self.window?.makeKeyAndVisible()
            return true
        }
    }
    class Page1: UIViewController{
        var c : UIButton!
        override func viewDidLoad() {
            super.viewDidLoad()
            c = UIButton()
            c.setTitle("Page1",for: .normal)
            c.frame = CGRect(x: 10, y: 50, width: 200, height: 50)
            view.addSubview(c)
            c.backgroundColor = .blue
            let page2 = Page2()
            self.addChildViewController(page2)
            page2.view.frame = CGRect(x: 10, y: 100, width: 200, height: 50)
            view.addSubview(page2.view)
        }
    }
    class Page2: UIViewController{
        var c : UIButton!
        var count = 0
        override func viewDidLoad() {
            super.viewDidLoad()
            c = UIButton()
            c.backgroundColor = .red
            c.frame = CGRect(x: 0, y: 0, width: 200, height: 50)
            c.setTitle("0",for: .normal)
            c.addTarget(self, action: #selector(buttonAction(_:)), for: .touchUpInside)
            view.addSubview(c)
        }
        func buttonAction(_ sender:UIButton!){
            self.count +=  1
            c.setTitle(self.count.description,for: .normal)
        }
    }

本案例涉及到2个View Controller类,分别为Page1、Page2。Page2中有一个按钮,点击时会给按钮的标题的数字加1。而Page1作为Root View Controller,嵌入到Window内,占据全部屏幕大小,它内嵌一个按钮和Page2——也就是另外一个View Controller。为了让Page2的视图可以显示在Page1内,需要把Page2.view属性作为子视图加入到Page1.view内,并且通过:

    self.addChildViewController(page2)

把Page2作为Page1的Child View Controller,这样就可以重用Page2的已有的逻辑。

作为好奇的程序员,不妨试试删除这一行代码,你会发现在点击Page2的按钮时,数字不会增加,因此说明当此代码行被删除,因此两个View Controller并没有任何关系时,它的操作代码不能被执行到。

一般很少直接使用UIViewController,更多的是直接使用特定目的的View Controller,也就是UIViewController的子类。常用的特定目的的类有这些:

  1. AlertController,警示框控制器
  2. NavigationController,导航控制器
  3. PageViewController,分页控制器
  4. TabBarController,标签页控制器

后文会继续介绍。