顯示廣告
隱藏 ✕
看板 KnucklesNote
作者 Knuckles (站長 那克斯)
標題 [Xcode][Swift3] 使用 ContainerView 切換子頁面
時間 2017-04-04 Tue. 23:45:11


依照前一篇 [Xcode][Swift3] 使用 ScrollView 建立水平捲動選單 - KnucklesNote板 - Disp BBS
使用水平選單產生選取頁籤的效果
但下方只有一個 TableView,只能簡單的更換不同的資料來顯示

如果想在下方建立一個子頁面,可以切換成不同的 Controller 的話
要使用 Container View


在 storyboard 使用 Container View 載入預設的第一頁

刪掉原本的 TableView 元件,拉一個 Container View 進來
[圖]

可以看到 Container View 旁邊會附帶一個相同大小的 View Controller

對 Container View 加上四個方向為0的 Constraints
[圖]


接下來可以在附帶的 View Controller 中加上 TableView 元件
或是改成使用 Table View Controller

要改用 Table View Controller 的話
先刪掉附帶的 View Controller,拉一個 Table View Controller 進來
[圖]


按著 Ctrl 將 Container View 拉至 Table View Controller
跳出的選單選擇「Embed」
[圖]

這樣即可將 Container View 改為附帶一個 Table View Controller

點一下連接的 Segue,在屬性檢視器輸入 Identifier 為「ContainerViewSegue」
之後在程式會用到
[圖]


新增一個 Table View Controller 的程式檔
點 command+n 新增一個 Cocoa Touch Class

Class 名稱輸入「Page1ViewController」
Subclass of 「UITableViewController」
[圖]


新增好程式檔後,要到 storyboard 設定自訂類別為「Page1Viewcontroller」
然後在 Storyboard ID 輸入「Page1」,之後在程式會用到
[圖]


設定 Table View Cell 的 Identifier 為「TableViewCell」
[圖]


有使用下拉更新的話,在 Table View Controller 的屬性檢視器
開啟並設定下拉更新的功能
[圖]



編輯程式檔 Page1ViewController.swift
將原本寫在 ViewController.swift 中關於 TableView 的程式改寫在這裡
像這樣
class Page1ViewController: UITableViewController {

    func loadData() {
        print("load data")

        if (self.refreshControl?.isRefreshing)! {
            self.refreshControl?.endRefreshing()
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        self.refreshControl?.addTarget(self, action: #selector(loadData), for: UIControlEvents.valueChanged)
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }

    // MARK: - Table view data source

    override func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 20
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "TableViewCell", for: indexPath)
        cell.textLabel?.text = "測試標題 \(indexPath.row)"
        return cell
    }
注意 Table View Controller 已有內建 refreshControl
所以不用在成員變數新增,只要在 viewDidLoad() 設定要執行的函數即可

原本寫在 ViewController.swift 中關於 TableView 的程式就可以刪除了

執行看看
[圖]



使用程式載入其他頁

在 storyboard 中,使用 Container View 只能內嵌一個 View Controller 而已
要切換為內嵌其他的 View Controller 的話只能用程式達成

假設其他四頁都只要使用簡單的 View Controller 就好
新增四個繼承 ViewController 的類別程式檔,檔名分別為
Page2ViewController.swift
Page3ViewController.swift
Page4ViewController.swift
Page5ViewController.swift

在 storyboard 拉四個 View Controller 進來
調整 Background 為不同的顏色以示區別
[圖]


此時會出現一個警告訊息,說這幾個 View Controller 沒有進入點
只要有設定 Storyboard ID 就可以消除這個警告

設定每頁的自訂類別,以及 Storyboard ID
例如第5頁的自訂類別為 Page5ViewController,Storyboard ID 為 Page5
[圖]



再來要寫程式來切換要顯示的頁面

先使用 Assistant edior 新增 Contain View 的 @IBOutlet
名稱輸入「containerView」
[圖]


編輯程式檔 ViewController.swift

新增成員變數
    // 1.
    var page1ViewController: Page1ViewController!

    // 2.
    lazy var page2ViewController: Page2ViewController = {
        self.storyboard!.instantiateViewController(withIdentifier: "Page2" as! Page2ViewController
    }()
    lazy var page3ViewController: Page3ViewController = {
        self.storyboard!.instantiateViewController(withIdentifier: "Page3" as! Page3ViewController
    }()
    lazy var page4ViewController: Page4ViewController = {
        self.storyboard!.instantiateViewController(withIdentifier: "Page4" as! Page4ViewController
    }()
    lazy var page5ViewController: Page5ViewController = {
        self.storyboard!.instantiateViewController(withIdentifier: "Page5" as! Page5ViewController
    }()

    // 3.
    var selectedViewController: UIViewController!
1. page1ViewController 因為有使用 Segue 連結了,後面會使用 prepare() 來取得

2. 其他四頁使用 Storyboard ID 來取得頁面的 Controller,存為成員變數
   前面加 lazy 代表變數第一次使用到的時候才會去取得初始值
   後面接一個立即執行的匿名函數,因為內容只有一行所以可以不用加 return

3. 使用成員變數 selectedViewController 來記錄目前選取的是哪一頁


新增成員函數 prepare(for:sender:)
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "ContainerViewSegue" {
            page1ViewController = segue.destination as! Page1ViewController
        }
    }
使用連結兩頁面的 Segue 來取得 page1ViewController


在 viewDidLoad() 裡加上
    selectedViewController = page1ViewController
設定預設選取的是第一頁


新增成員函數 changePage(to:)
    // 1.
    func changePage(to newViewController: UIViewController) {
        // 2. Remove previous viewController
        selectedViewController.willMove(toParentViewcontroller: nil)
        selectedViewController.view.removeFromSuperview()
        selectedViewController.removeFromParentViewController()

        // 3. Add new viewController
        addChildViewController(newViewController)
        self.containerView.addSubview(newViewController.view)
        newViewController.view.frame = containerView.bounds
        newViewController.didMove(toParentViewController: self)

        // 4.
        self.selectedViewController = newViewController
    }
1. 參數傳入新頁面的 Controller,會先移除 Container View 上附帶的舊 Controller
   再將新的 Controller 加到 Container View

2. 移除 Controller 前要先呼叫 willMove() 讓 Contrller 執行離開頁面的程式
   然後使用 removeFromSuperview() 將 Controller 移出 Container View
   最後使用 removeFromParentViewController 將 Contrller 移出主頁的 Controller

3. 先用 addChildViewController() 將新的 Controller 加進主頁的 Controller
   接著將新的 Controller 加進 Container View
   設定新 Controller 中頁面的尺寸為 Container View 的大小
   呼叫 didMove() 讓新的 Controller 執行頁面載入的程式

4. 修改目前選取的頁面為新的 Controller


修改五個按鈕的 @IBAction 為
    @IBAction func showPage1(_ sender: Any) {
        changeTab(to: page1Button)
        changePage(to: page1ViewController)
    }

    @IBAction func showPage2(_ sender: Any) {
        changeTab(to: page2Button)
        changePage(to: page2ViewController)
    }

    @IBAction func showPage3(_ sender: Any) {
        changeTab(to: page3Button)
        changePage(to: page3ViewController)
    }

    @IBAction func showPage4(_ sender: Any) {
        changeTab(to: page4Button)
        changePage(to: page4ViewController)
    }

    @IBAction func showPage5(_ sender: Any) {
        changeTab(to: page5Button)
        changePage(to: page5ViewController)
    }
在每個按鈕點擊後執行 changePage(to:)


執行看看
[圖]




參考
Cocoacasts Managing View CodePathControllers With Container View Controllers
CodePath Container View Controllers Quickstart



--
※ 作者: Knuckles 時間: 2017-04-04 23:45:11
※ 編輯: Knuckles 時間: 2017-04-09 01:13:58
※ 看板: KnucklesNote 文章推薦值: 0 目前人氣: 0 累積人氣: 4851 
分享網址: 複製 已複製
r)回覆 e)編輯 d)刪除 M)收藏 ^x)轉錄 同主題: =)首篇 [)上篇 ])下篇