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 的視圖控制器。
相關文章
相關標籤/搜索