iOS14开发-UIViewController

介绍

UIViewController 能够理解为 App 的界面,负责管理 UIView 中显示的内容和用户的交互,主要有如下做用:swift

  • 负责建立和管理 UIView。
  • 响应用户与视图的交互。
  • 负责界面的切换与传值。
  • 响应设备的方向变化。
  • 有一些特殊的视图控制器(导航控制器、标签栏控制器)能够更加方便和规范地管理 UIView。

建立

storyboard

  • 初始化箭头指向的 UIViewController。
let vc = UIStoryboard(name: "storyboard名", bundle: nil).instantiateInitialViewController()
复制代码
  • 初始化其余的 UIViewController。
let vc = UIStoryboard(name: "storyboard名", bundle: nil).instantiateViewController(withIdentifier: "Storyboard ID")
复制代码

纯代码

let vc = UIViewController()
复制代码

xib

这种方式本质是 xib 建立 UIView,而后让这个 UIView 成为 UIViewController 的默认 View。markdown

  • 建立 UIViewController 的时候勾选了Also create XIB file,能够直接经过下面两种方式初始化:
// 方式一
let vc = UIViewController()

// 方式二
let vc = UIViewController(nibName: "xib的名字", bundle: nil)
复制代码
  • 若是 UIViewController 与 xib 分别建立,直接使用上面的两种方式会报错,由于这种方式还须要本身处理 2 件事:

(1)将 xib 文件 的File’s Owner的类绑定为 UIViewController。 (2)将File’s Ownerview属性设置为xib文件(拽线设置便可)。闭包

view属性

入门知识里初步介绍了 UIViewController 与其属性view的关系,其实它们之间的关系没有那么简单,须要进一步分析。app

生命周期顺序

initinit(nibName...)(初始化分配内存)—> loadView(加载view)—> viewDidLoad(view已经加载)—> viewWillAppear(view即将显示)—> viewWillLayoutSubviews(将要布局子view)—> viewDidLayoutSubviews(已经布局子view)—> viewDidAppear(view已经显示)—> viewWillDisappear(view即将消失)—> viewDidDisappear(view已经消失)—> dealloc(释放内存)
复制代码

延迟加载

UIViewController 的 view 的延迟加载:第一次使用的时候才会去加载,并非建立 UIViewController 时加载。ide

  • 验证:经过纯代码跳转时发现屏幕黑色且卡顿,设置颜色后正常。

loadView方法

  • 用于建立 UIViewController 的 view。
  • 当 UIViewController 访问 view 时若是发现为 nil,就会调用 loadView 方法。
  • loadView 方法执行完会自动执行 viewDidLoad。
  • loadView 方法大概的实现思路以下:
func loadView() {   
    // 若是UIViewController是经过storyboard建立的,从storyboard中加载视图来建立view
    if storyboard建立 {
        // ...
        return
    }
    
     // 若是UIViewController是经过xib建立的,从xib中加载视图来建立view
    if xib建立 {
        // ...
        return
    }
    
    // 若是上面都不是,则会建立一个普通的view视图
    let view = UIView(frame: UIScreen.main.bounds)    
    self.view = view
}
复制代码

重写loadView方法

  • 该方法要么不重写,若是重写必定要注意:
    • 必须在方法里给 UIViewController 的 view 赋值。
    • 不要调用super.loadView()
    • 不要手动调用该方法。
override func loadView() {
    let myView = UIView(frame: UIScreen.main.bounds)
    view = myView
}
复制代码
  • 一旦重写,其余建立 view 的方式都会失效,由于决定 UIViewController 的 view 优先级为:loadView > storyboard > nibName > xib。

跳转

从一个 UIViewController 跳转到另外一个 UIViewController 有两种方式,分别为模态跳转导航跳转布局

模态跳转

storyboard

  • 直接拽线,选择Present Modally,这根线是一个 UIStoryboardSegue 对象(简称 Segue),能够设置相关的属性。
  • 自动型 Segue
    • 直接跳转,无需条件。
    • 经过当前 UIViewController 某个具体的控件(如按钮)拽线到另外一个 UIViewController。
  • 手动型 Segue
    • 从当前 UIViewController 拽线到另外一个 UIViewController,须要给这根线设置identifier
    • 在程序中须要跳转的地方调用performSegue(withIdentifier: , sender:)方法完成跳转。

纯代码

  • 跳转界面:present
  • 返回界面:dismiss
  • iOS 13 以后,模态跳转并不是全屏显示,若是须要全屏显示,须要手动设置。

两个概念

  • presentedViewController: 被 present 的控制器。
  • presentingViewController:正在 presenting 的控制器。

导航跳转

这种操做的前提是 UIViewController 包含在 UINavigationController 中。post

storyboard

  • 直接拽线,选择Show
  • 自动型 Segue 和 手动型 Segue 跟模态跳转同样。

纯代码

  • 跳转界面
    • navigationController?.pushViewController
  • 返回界面
    • 左上角的返回按钮。
    • 屏幕边缘滑动。
    • navigationController?.popViewController

传值

顺向传值

顺向传值即按照 UIViewController 跳转的顺序进行传值,好比控制器A跳转到控制器B,A向B的传值就是顺向传值。顺向传值只须要在目标控制器中声明须要接收的参数,而后在源控制器中进行传值便可。ui

  • storyboard 方式。
  • 代码方式。

逆向传值

逆向传值即按照 UIViewController 跳转的顺序反向进行传值,好比控制器A跳转到控制器B,控制器B在返回控制器A时进行传值,这种方式就是逆向传值。逆向传值不能像顺向传值那样简单进行,须要借助于下面三种方式。spa

代理

代理模式须要弄清楚被代理对象和代理对象,而后按照下面的规范进行。设计

  • 被代理对象(须要传值的 UIViewController)
    • 声明协议,在协议中定义传值方法,方法的参数个数与类型取决于须要传值的个数和类型。
    • UIViewController 中声明一个代理属性。
    • 在须要传值的地方调用代理属性的方法完成传值。
  • 代理对象(接收值的 UIViewController)
    • 实现被代理对象声明的协议,实现协议中的方法,拿到传过来的值进行使用。
    • 须要设置当前的 UIViewController 为被代理 UIViewController 中的代理属性。

闭包

能够理解为代理模式中协议的闭包替代,比代理模式更简单。

  • 须要传值的 UIViewController
    • 声明一个闭包属性,闭包的参数个数与类型取决于须要传值的个数和类型,闭包的返回值通常为 Void。
    • 在须要传值的地方调用闭包完成传值。
  • 接收值的 UIViewController
    • 实现须要传值的 UIViewController 中的闭包属性,在闭包的实现中拿到传过来的值进行使用。

通知

  • 接收值的 UIViewController 经过监听通知捕获传过来的值。
NotificationCenter.default.addObserver(self, selector: #selector(handlerNoti), name: NSNotification.Name("abc"), object: nil)
复制代码
  • 须要传值的 UIViewController 将值经过通知的方式发送出去。
NotificationCenter.default.post(name: NSNotification.Name("abc"), object: nil, userInfo: ["info" : inputTf.text!])
复制代码
  • 须要先监听,后发送
  • iOS 9 以后 NSNotificationCenter 无需手动移除观察者。

常见ViewController

UIAlertController

  • 警告(对话框)控制器。
  • 用一个对话框进行信息的提示,经过模态形式弹出。
  • 有两种样式:alertactionSheet
  • 按钮经过 UIAlertAction 添加,有 3 种样式:defaultcanceldestructive,一个 UIAlertController 中只能有一个cancel样式的 UIAlertAction。
  • 基本使用
class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
    }

    @IBAction func showAlert(_ sender: Any) {
        let alertVC = UIAlertController(title: "舒适提示", message: "天气转凉,你们注意保暖,当心感冒", preferredStyle: .alert)

        let ok = UIAlertAction(title: "OK", style: .default) { _ in
            print("点击了ok")
        }

        let cancel = UIAlertAction(title: "Cancel", style: .cancel) { _ in
            print("点击了cancel")
        }

        alertVC.addAction(ok)
        alertVC.addAction(cancel)

        present(alertVC, animated: true, completion: nil)
    }

    @IBAction func showSheet(_ sender: Any) {
        let alertVC = UIAlertController(title: "选择头像", message: "请选择合适的方式来处理", preferredStyle: .actionSheet)

        let ok = UIAlertAction(title: "相册", style: .default) { _ in
            print("用户选择了相册")
        }

        let des = UIAlertAction(title: "拍照", style: .destructive) { _ in
            print("用户选择了拍照")
        }

        let cancel = UIAlertAction(title: "取消", style: .cancel) { _ in
            print("点击了取消")
        }

        alertVC.addAction(ok)
        alertVC.addAction(des)
        alertVC.addAction(cancel)

        present(alertVC, animated: true, completion: nil)
    }
}
复制代码
  • 登陆案例:用 UIAlertController 代替 print 打印。

UINavigationController

  • 导航控制器。
  • 能够展现多个 UIViewController,这些 UIViewController 是层级关系。
  • 它的 View 由三部分组成,最上面的UINavigationBar,最下面默认隐藏的UIToolBar,中间是 UIViewController 的view
  • 经过栈管理 UIViewController:先进后出。
    • pushViewController:压栈。
    • popViewController:出栈。
  • 经过 UINavigationItem 设置 title、leftBarButtonItem、rightBarButtonItem等。

UINavigationBar和UINavigationItem的关系

  • UINavigationBar是 UINavigationController 的属性,其属性设置会影响内部全部的 UIViewController。
  • UINavigationItem是 UIViewController 的属性,用于配置当前 UIViewController 显示时UINavigationBar上显示的内容。
  • UINavigationBar内部也维持一个栈,栈中存放的是一个个 UINavigationItem。当一个 UIViewController push 到 UINavigationController 时,它的UINavigationItem也会被 push 进 UINavigationBar的栈。所以UINavigationBar的栈和 UINavigationController 的栈一一对应。

UINavigationBar 的内容显示

标题
  • 若是当前 UIViewController 设置了titleView属性,则展现标题视图。
  • 若是当前 UIViewController 设置了title属性,则显示标题文字。
  • 若是都没设置,则显示空白。
  • iOS11 以后能够设置大标题。能够经过 storyboard 直接设置,也能够经过以下的代码设置:
// 全部界面显示大标题
navigationController?.navigationBar.prefersLargeTitles = true
// 当前界面是否显示大标题,never表示不显示大标题即显示小标题 
navigationItem.largeTitleDisplayMode = .never
复制代码
右侧按钮
  • 若是当前 UIViewController 设置了rightBarButtonItem属性,则显示右侧按钮,不然显示空白。
左侧按钮
  • 若是当前 UIViewController 设置了leftBarButtonItem属性,则显示左侧按钮。
  • 若是前一个 UIViewController 设置了backButtonItem属性,则显示返回按钮。
  • 若是前一个 UIViewController 设置了title属性,则显示标题文字封装的返回按钮。
  • 若是以上都未设置,则展现文字Back封装的返回按钮。

注意:默认状况下返回按钮和左侧按钮是不一样时显示的,只显示返回按钮而不显示左侧按钮。

返回按钮
  • 若是当前 UIViewController 设置了leftBarButtonItem属性,则默认的返回按钮会被替代,自带的返回和从屏幕边缘滑动返回的效果失效,此时只能经过popViewController返回。
  • 若是前一个 UIViewController 设置了backButtonItem属性或设置了backButtonTitle,能够起到更改返回按钮文字和图片的目的,可是返回按钮的<图标会一直存在,这种方式自带的返回和从屏幕边缘滑动返回的效果依然有效。
颜色问题
  • UINavigationBar 的颜色:能够经过 UINavigationBar 的barTintColor设置。
  • UINavigationBar 上面内容的渲染颜色:默认状况下,按钮或系统图片按钮都会渲染成蓝色,能够经过 UINavigationBar 的tintColor设置。

案例

  • storyboard 使用。
  • 纯代码使用。
  • 自定义使用。

UITabBarController

  • 标签栏控制器。
  • 能够展现多个 UIViewController,这些 UIViewController 是平级关系。但展现的 UIViewController 最多不超过5个,不然会折叠。
  • 它的 View 由两部分组成,上面是 UIViewController 的view,下面是UITabBar
  • 经过addChildViewController添加 UIViewController,经过UIViewController 的UITabBarItem属性设置展现的文字、默认图片、选中图片和角标。
  • 默认已经实现了UITabBarDelegate

UITabBarControllerDelegate

  • UITabBarController 还提供一个代理属性,经过它能够设置一个代理 UITabBarControllerDelegate。
  • 监听切换 UIViewController
    • 经过 UITabBarDelegate 的tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem)方法。
    • 经过 UITabBarControllerDelegate 的tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController)方法。

颜色问题

UITabBar的颜色

能够经过 UITabBar 的barTintColor设置。

渲染颜色
  • 图片通常由设计师统一设计,须要设置标题文字颜色以适应图片。
  • 方式一:每一个 UIViewController 单独设置。
// 默认文字颜色
vc.tabBarItem.setTitleTextAttributes([NSAttributedString.Key.foregroundColor : UIColor.white], for: .normal)
// 选中文字颜色
vc.tabBarItem.setTitleTextAttributes([NSAttributedString.Key.foregroundColor : UIColor.orange], for: .highlighted)
复制代码
  • 方式二:Appearance统一设置。
let item = UITabBarItem.appearance()
// 默认文字颜色
item.setTitleTextAttributes([NSAttributedString.Key.foregroundColor : UIColor.white], for: .normal)
// 选中文字颜色
item.setTitleTextAttributes([NSAttributedString.Key.foregroundColor : UIColor.orange], for: .highlighted)
复制代码
  • 方式三:iOS 10 以后能够统一设置选中和未选中颜色。(推荐使用
// 选中的图片文字颜色
vc.tabBarController?.tabBar.tintColor = UIColor.orange
// 未选中的文字颜色
vc.tabBarController?.tabBar.unselectedItemTintColor = UIColor.white

// 角标的背景色
vc.tabBarItem.badgeColor = UIColor.orange
// 角标的颜色
vc.tabBarItem.badgeTextAttributes(for: .normal) = UIColor.white
复制代码

案例

  • storyboard 使用。
  • 纯代码使用。
  • 自定义使用。

其余

  • UITableViewController:表视图控制器,集成了 UITableView 的视图控制器。
  • UICollectionViewController:集合视图控制器,集成了 UICollectionView 的视图控制器。
相关文章
相关标签/搜索