顯示廣告
隱藏 ✕
看板 KnucklesNote
作者 Knuckles (站長 那克斯)
標題 [Xcode][Swift3] 使用 PageViewController 滑動換頁
時間 2017-04-07 Fri. 01:36:58


延續上一篇 [Xcode][Swift3] 使用 ContainerView 切換子頁面 - KnucklesNote板 - Disp BBS
我們可以使用上方的選單切換下方要載入的子頁面了

如果想要使用左右滑動換頁的話
那就要再加上 Page View Controller

將原本連結 Page1ViewController 的 Segue 刪除
拉一個 Page View Controller 進來
[圖]


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


點一下 Segue 在屬性檢視器
輸入 Identifier 為「ContainerViewSegue」
[圖]


按 Command+n 新增 Cocoa Touch Class
類別名稱輸入「PageViewController」
Subclass of:「UIPageViewController」
[圖]


設定 Page View Controller 的自訂類別為「PageViewController」
[圖]


在 Page View Controller 的屬性檢視器設定換頁類型為「Scroll」滑動換頁
[圖]



修改剛剛新增的 PageViewController.swift

新增成員變數
    lazy var page1ViewController: Page1ViewController = {
        self.storyboard!.instantiateViewController(withIdentifier: "Page1" as! Page1ViewController
}()
    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
    }()

    lazy var orderedViewControllers: [UIViewController] = {
        [self.page1ViewController, self.page2ViewController, self.page3ViewController, self.page4ViewController, self.page5ViewController]
    }()
就是把本來寫在 ViewController 中取得五個子頁面的程式改寫在這邊
然後再把這五個子頁面存成一個 UIViewController 陣列 orderedViewControllers


將類別加上繼承 UIPageViewControllerDataSource
修改 class PageViewController: UIPageViewController { 這行為
class PageViewController: UIPageViewController, UIPageViewControllerDataSource {

在 viewDidLoad() 裡加上
        self.dataSource = self
        setViewControllers([page1ViewController], direction: .forward, animated: false, completion: nil)
使用 setViewControllers() 指定 page1ViewController 為預設要顯示的 Controller

第一個參數要傳入一個 UIViewController 陣列
是用在像是 eBook 的 App,一頁有兩個子頁面的情況
如果一頁只有一個子頁面的話,就傳入一個只有一個值的陣列即可


加上兩個 UIPageViewControllerDataSource 的成員函數
    // MARK: - UIPageViewControllerDataSource

    func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
        guard let index = orderedViewControllers.index(of: viewController) else {
            return nil
        }
        let previousIndex = index - 1
        guard previousIndex >=0 && previousIndex < orderedViewControllers.count else {
            return nil
        }
        return orderedViewControllers[previousIndex]
    }

    func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
        guard let index = orderedViewControllers.index(of: viewController) else {
            return nil
        }
        let nextIndex = index + 1
        guard nextIndex >=0 && nextIndex < orderedViewControllers.count else {
            return nil
        }
        return orderedViewControllers[nextIndex]
    }
第一個函數是在往左滑動時,輸入參數為目前的 ViewController
我們要提供左邊的 ViewController 是哪一個

利用之前存的 ViewController 陣列 orderedViewControllers
取得目前的 ViewController 在陣列中的 index
將 index - 1 後,就可以取得左邊的 ViewController

第二個函數就是要取得右邊的 ViewController 為何


再來修改主頁面的程式檔 ViewController.swift

因為換頁的動作改到 PageViewController 去執行了
所以可以將之前寫的換頁程式都刪除,或是註解掉

將這幾個成員函數 mainStoryboard、page1ViewController ~ page5ViewController、
selectedViewcontroller 刪除

將成員函數 changePage() 與 prepare() 刪除

將 viewDidLoad() 中的 selectedViewController = page1ViewController 這行刪除

在五個按鈕的 @IBAction 中,將 changePage(to: ...) 刪除


執行看看
[圖]

可以左右滑動換頁了,只是上方的選單不能點,也不會跟著改變選取狀態


點擊選單讓 PageViewController 換頁

在要主頁面點擊選單,讓子頁面的 PageViewController 執行換頁的話
要使用 Segue 取得子頁面的 PageViewController
然後執行 PageViewController 的 setViewControllers()

先在 PageViewcontroller.swift 加上成員函數 showPage()
    func showPage(byIndex index: Int) {
        let viewController = orderedViewControllers[index]
        setViewControllers([viewController], direction: .forward, animated: false, completion: nil)
    }
使用子頁面陣列的索引值取得要顯示哪個子頁面後
執行 setViewControllers() 來換頁


修改主頁面的程式檔 ViewController.swift

加上成員變數
    var pageViewController: PageViewController!

加上成員函數
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "ContainerViewSegue" {
            pageViewController = segue.destination as! PageViewController
        }
    }
使用 Segue 取得 Container View 內嵌的 PageViewController


現在我們可以在主頁面 ViewController
執行 PageViewController 的成員函數 showPage() 來換頁了

修改五個按鈕的 @IBAction 為
    @IBAction func showPage1(_ sender: Any) {
        changeTab(to: page1Button)
        pageViewController.showPage(byIndex: 0)
    }

    @IBAction func showPage2(_ sender: Any) {
        changeTab(to: page2Button)
        pageViewController.showPage(byIndex: 1)
    }

    @IBAction func showPage3(_ sender: Any) {
        changeTab(to: page3Button)
        pageViewController.showPage(byIndex: 2)
    }

    @IBAction func showPage4(_ sender: Any) {
        changeTab(to: page4Button)
        pageViewController.showPage(byIndex: 3)
    }

    @IBAction func showPage5(_ sender: Any) {
        changeTab(to: page5Button)
        pageViewController.showPage(byIndex: 4)
    }

執行看看
[圖]

可以點選單換頁了,而且換頁後也可以用左右滑動換頁
但是滑動換頁後,選單的選取狀態沒有改變


滑動換頁後改變選單的選取狀態

要在滑動換頁後改變選單的選取狀態
首先要能在 PageViewController 中取得主頁面的 ViewController

修改 PageViewController.swift

新增成員變數
    var mainViewController: ViewController!


修改 ViewController.swift

在成員函數 prepare() 中
pageViewController = segue.destination ... 的下一行加上
        pageViewController.mainViewController = self
在主頁面使用 Segue 取得子頁面位址的時候
也將主頁面的位址記錄在子頁面的成員變數

在主頁面 ViewController.swift 新增成員函數
    func changeTab(byIndex index: Int) {
        switch index {
        case 0: changeTab(to: page1Button)
        case 1: changeTab(to: page2Button)
        case 2: changeTab(to: page3Button)
        case 3: changeTab(to: page4Button)
        case 4: changeTab(to: page5Button)
        default: return
        }
    }
使用陣列的索引值呼叫之前寫的 changeTab(to:) 來改變按鈕選取的狀態


現在我們可以在 PageViewController 中使用
mainViewController.changeTab(byIndex:)
來改變頁籤的選取狀態了

要能在左右滑動換頁後,執行換頁籤的動作
要加上 UIPageViewController 的 Delegate 函數

修改 PageViewController.swift

將 class PageViewController: UIPageViewController ... 這行改為
class PageViewController: UIPageViewController, UIPageViewControllerDataSource, UIPageViewControllerDelegate {
加上繼承 UIPageViewControllerDelegate

加上一個成員變數
    var willTransitionTo: UIViewController!
用來記錄將要移至的子頁面

在成員函數 viewDidLoad() 中加上
        self.delegate = self

加上兩個 Delegate 函數
    func pageViewController(_ pageViewController: UIPageViewController, willTransitionTo pendingViewControllers: [UIViewController]) {
        self.willTransitionTo = pendingViewController.first
    }

    func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimation finished: Bool, previousVieControllers: [UIViewController], transitionCompleted completed: Bool) {
    if completed {
        guard let index = orderedViewControllers.index(of: self.willTransitionTo) else { return }
        let previousViewController = previousViewControllers.first!
        let previousIndex = orderedViewControllers.index(of: previousViewController)
        if index != previousIndex {
            mainViewController.changeTab(byIndex: index)
        }
    }
第一個函數會在左右滑動換頁剛開始時執行
這邊可以取得目標的子頁面為何,將他存在成員變數 willTransitionTo

第二個函數會在左右滑動換頁結束時執行
參數 completed 代表是否有完成換頁的動作
有的話再利用成員變數 willTransitionTo 記錄的目標子頁面
取得子頁面在陣列 orderedViewControllers 的索引值 index
然後執行 mainViewController.changeTab(byIndex: index) 切換頁籤狀態

若是滑到下一頁時,又快速滑回上一頁,這樣 willTransitionTo 只會取到下一頁
所以要加上 if index != previousIndex 避免移動到錯誤的頁籤


執行結果
 


程式碼已上傳至
https://github.com/KnucklesHuang/Swift-HorizontalScrollBar



參考
How to Use UIPageViewController in Swift
StackOverflow Pass data between ViewController and ContainerViewController



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