看板 KnucklesNote
作者 標題 [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 中取得五個子頁面的程式改寫在這邊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]
}()
然後再把這五個子頁面存成一個 UIViewController 陣列 orderedViewControllers
將類別加上繼承 UIPageViewControllerDataSource
修改 class PageViewController: UIPageViewController { 這行為
class PageViewController: UIPageViewController, UIPageViewControllerDataSource {
在 viewDidLoad() 裡加上
self.dataSource = self
setViewControllers([page1ViewController], direction: .forward, animated: false, completion: nil)
使用 setViewControllers() 指定 page1ViewController 為預設要顯示的 ControllersetViewControllers([page1ViewController], direction: .forward, animated: false, completion: nil)
第一個參數要傳入一個 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]
}
第一個函數是在往左滑動時,輸入參數為目前的 ViewControllerfunc 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 陣列 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)
}
使用子頁面陣列的索引值取得要顯示哪個子頁面後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 內嵌的 PageViewControllerif segue.identifier == "ContainerViewSegue" {
pageViewController = segue.destination as! 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)
}
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:) 來改變按鈕選取的狀態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
}
}
現在我們可以在 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)
}
}
第一個函數會在左右滑動換頁剛開始時執行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 累積人氣: 2939
回列表(←)
分享