使用UIPageViewController進行多頁面管理

使用UIPageViewController進行多頁面管理

Do it step by step!

在制作app的引導頁的時候會用到UIPageViewController,有多個水平關系的ViewController也能夠用UIPageViewController來管理。這個例子使用的數據來自豆瓣的開放API,會使用Alamofire去請求數據,并用SDWebImage加載圖片,因此你的電腦上需要安裝有Cocoapods,方便第三方庫的引入。

前期準備

1.新建一個項目,選擇Single View Application


2.Product Name填PageViewControllerDemo,語言選擇Swift

3.右鍵點擊ViewController.swift代碼文件,將它刪掉,在彈出的提示框中選擇Move To Trash

4.使用Command + N快捷鍵新建一個文件,選擇Cocoa Touch Class

5.創建一個UIViewController的子類,將它命名為MainController。在接下來的部分,將會看到MainController的作用是用來給UIPageViewController提供相應的ViewController,并對頁面改變做出響應。

6.打開Main.storyboard,現在只有一個Scene在Main.storyboard中,選中這個scene讓它和MainController.swift代碼文件關聯

暫時把項目關掉,引入第三方庫之后,再重新打開。
接下來我們開始使用Cocoapods引入Alamofire和SDWebimage
7.使用終端進入項目所在的目錄,使用cd <項目路徑>命令,回車之后就進入了項目所在目錄

8.在終端敲入這條命令touch Podfile,回車,這樣就創建了Podfile文件

9.打開項目文件夾,你會發現多了一個Podfile文件,雙擊打開Podfile,將下面這段安裝命令復制到Podfile中

platform :ios, '8.0'
use_frameworks!

pod 'Alamofire'
pod 'SDWebImage'

10.關閉Podfile,回到終端,在終端里面敲入pod install命令,回車,安裝第三方庫。
等待....安裝完成之后,會有和截圖類似結果。


請注意終端里的最后一句綠色的文字,它說從現在開始要使用PageViewControlerDemo.xcworkspace來打開項目了,因此以后打開這個項目時不能再使用雙擊PageViewControlerDemo.xcodeproj的方式。


布局階段

1.打開項目,并打開storyboard,選中MainController,將它嵌入到NavigationController中。


2.從控件庫中拖3個Button到MainController,把它們平鋪在navigationBar的下方,這些Button用來切換不同的頁面。

3.同時選中這3個Button,改變它們的一些屬性

屬性 相應的值
寬度 高度 200,40
背景顏色 RGB(248, 248, 248)
字體顏色 黑色

完成之后,如下所示


4.接下來為button添加約束,以下是我們需要實現的約束

  • button位于navigationBar的正下方
  • 每個button的寬度為屏幕寬度的1/3
  • 每個button等高等寬
  • 彼此相鄰

選中最左邊的button,點擊設計面板右下方的第三個按鈕,給它添加左邊,上邊,以及高度這三個約束


按住control鍵,用鼠標左鍵從button中往空白處拖,可以看到一條藍色的線,松開鼠標左鍵,在彈出菜單中選擇Equal Widths


我們要button的寬度為屏幕的1/3,而不是完全等于屏幕寬度,因此要修改一個系數,在Document outline中選中Botton.width約束,將Multiplier改為1/3


接下來為剩下的兩個button添加約束,選中第二個button,按住control鍵,用鼠標左鍵向第一個button拖,放開左鍵,按住shift鍵,在彈出菜單中選擇Horizontal Spacing,Top,Equal Widths,Equal Heights,最后點擊Add Constraints

最后一個button也用同樣的方法給它加上約束

最后在不同的模擬器運行一遍看看有什么效果,或者用預覽的方式看也行。可以看到button是均勻排列的


5.添加當前頁面提示條,在button的下方會有一個紅色的條,用來提醒當前是哪個頁面,順便把的button的Title分別改為(電影,音樂,圖書),因為使用了豆瓣的API獲取了這三種不同的數據。
首先從控件庫里面拉一個View到button的下方,為了看的清楚一點,給View的設置一個深一點的背景色,選中它,添加以下4個約束,在Update Frames選擇Items of New Constraints,最后點添加

6.在MainController中嵌入一個UIPageViewController,讓UIPageViewController成為MainController的SubViewController,執行以下的5個操作

  • 從控件庫中找到Container View,并把它拖到MainController的空白處
  • 會發現多了一個ViewController,把它刪掉
  • 選中Container View,給它添加上下左右都為0的4個約束
  • 從控件庫中找到Page View Controller,并把它拖到設計面板的空白處
  • 選中Container View,按住control鍵,用鼠標左鍵向第一個Page View Controller拖,放開左鍵,從彈出菜單中選擇Embed

經過上面的5個步驟,就把Page View Controller嵌入到MainController中了,這個過程也可以用代碼來實現的。下面是完成之后的截圖


7.接下開是3個真正要展示內容的頁面,因為3個頁面的布局一摸一樣,因此只把第一個頁面的步驟記下來,剩下的兩個是相同的。
在控件庫中拖一個View Controller到設計面板的空白處,選中這個剛添加的View Controller,把它的Storyboard ID設置為MovieControllerID

在這個View Controller中添加一個TableView,改變TableView的大小,讓它鋪滿整個View Controller,然后點擊設計面板右下方的最后一個布局按鈕,在彈出菜單中選擇Add Missing Constraints

這時新建一個代碼文件和這個View Controller關聯起來。Command + N,選擇Cocoa Touch Class,創建一個UIViewController的子類,把它命名為MovieController,最后回到storyboard把View Controller和MovieController關聯起來,這樣就可以在MovieController.swift代碼文件中添加代碼來控制相應的View Controller

8.執行第7步的內容,創建接下來的兩個頁面,對應如下

StoryBoard ID 代碼文件名 關聯的View Controller
MovieControllerID MovieController.swift Movie Controller
MusicControllerID MusicController.swift Music Controller
BookControllerID BookController.swift Book Controller

完成后代碼文件的結構,以下是完成上面步驟后的代碼文件結構圖,和storyboard截圖



還差一點點,布局工作就要完成了,加油?。。。?br> 9.創建一個通用的UITableViewCell的子類,用來顯示圖片的標題。創建一個新的文件,同樣選擇Cocoa Touch Class,不過這次要繼承UITableViewCell,把它命名為CommonCell,同時勾選Also create XIB file

10.打開CommonCell.xib,將Common Cell的高度設置為150

11.為Common Cell添加Image View和Label。執行以下步驟

  • 拖放一個Image View到Common Cell中,將它的寬高分別設置為120,150
  • 給Image View添加如下約束
  • 拖放一個Label到Common Cell中
  • 給Label添加如下約束,并把它的文字設置為居中
  • 完成之后


12.選中Common Cell,打開輔助視圖,為Image View和Label生成相應的outlet。Image View對應的outlet為coverImageView,Label對應的outlet為titleLabel



13.回到storyboard分別為Movie Controller,Music Controller,BookController的tableView生成相應的outlet。

View Controller Table View Outlet
Movie Controller movieTableView
Music Controller musicTableView
Book Controller bookTableView

14.選中MainController的button,從左到右依次把它們的tag設置為100,101,102


到這里為止所有的布局就結束了,接下來就是代碼部分了!


代碼環節

在寫代碼之前,先創建幾個分組,方便代碼的分類和管理。右鍵點擊PageViewControllerDemo分組,選擇New Group,輸入新的分組名Controller。用同樣的方式創建以下5個分組,View,DataModel,Other,Storyboard & XIB,Helper。


分組是組織代碼的一種方式,把具有相似功能的代碼放到一個分組中。完成后,代碼文件結構會和下面的截圖類似

現在開始寫代碼

1.右鍵Helper分組,新建一個SWift File,把它命名為GetDataFromDouBan


import Alamofire    //導入Alamofire, 以便執行網絡請求

class GetDataFromDouBan {
    
    /**
     用來從豆瓣開放平臺中獲取數據的幫助方法
     
     - parameter dataURL:         請求URL(對應電影,音樂,圖書的請求地址)
     - parameter type:            類型分別是subjects,musics,books
     - parameter keyword:         搜索關鍵字
     - parameter completedHandle: 數據接收到之后的處理函數
     */
    static func getData(dataURL: String, type: String, keyword: String, completedHandle: (data: [NSDictionary]) -> Void) {
        
        //請求數據
        Alamofire.request(.GET, dataURL, parameters: ["q": keyword, "count": "10"], encoding: ParameterEncoding.URL, headers: nil).responseJSON { (response: Response<AnyObject, NSError>) -> Void in
            
            //獲取返回的內容
            if let result = response.result.value {
                
                //拿到電影,音樂,圖書的信息
                if let data = result[type] as? [NSDictionary] {
                    //回到主線程,執行UI更新等操作
                    NSOperationQueue.mainQueue().addOperationWithBlock({ () -> Void in
                        completedHandle(data: data)
                    })
                }
            }
        }
    }  
}

上面的代碼看不懂也沒有關系,它的作用是用來獲取電影,音樂,圖書等數據的,拿到數據之后,就會執行completedHandle(data: data)函數,進行數據模型的搭建,UI的刷新等

2.回到MainController.swift代碼文件,這是我們的主控制器。首先添加一個屬性,通過這個屬性來引用我們嵌入到Main Controller的UIPageViewController

var pageViewController: UIPageViewController!

在viewDidLoad()方法中,添加下面這句代碼。它獲取MainController的所有子視圖控制器,因為只有一個,所以第一個就是UIPageViewController

//獲取到嵌入的UIPageViewController                                       
pageViewController = self.childViewControllers.first as! UIPageViewController

在pageViewController屬性的下面添加3個變量,以此來引用在前面創建的3個頁面

var pageViewController: UIPageViewController!
    
var movieController: MovieController!
var musicController: MusicController!
var bookController: BookController!

在viewDidLoad()方法中對這3個變量進行初始化,分別對應電影,音樂,圖書頁面

//根據Storyboard ID來創建一個View Controller
movieController = storyboard?.instantiateViewControllerWithIdentifier("MovieControllerID") as! MovieController
musicController = storyboard?.instantiateViewControllerWithIdentifier("MusicControllerID") as! MusicController
bookController = storyboard?.instantiateViewControllerWithIdentifier("BookControllerID") as! BookController

UIPageViewController是容器控制器,可是它自己卻不知道應該包含哪些View Controller,所以要有別人來告訴它。借助UIPageViewControllerDataSource中的兩個方法,告訴UIPageViewController應該在頁面切換的時候,顯示什么內容。在最后一個花括號的下方添加下面這段代碼

extension MainController: UIPageViewControllerDataSource {
    
    //返回當前頁面的下一個頁面
    func pageViewController(pageViewController: UIPageViewController, viewControllerAfterViewController viewController: UIViewController) -> UIViewController? {
        
        if viewController.isKindOfClass(MovieController) {
            return musicController
        }
        else if viewController.isKindOfClass(MusicController) {
            return bookController
        }
        return nil
        
    }
    
    //返回當前頁面的上一個頁面
    func pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController viewController: UIViewController) -> UIViewController? {
        
        if viewController.isKindOfClass(MusicController) {
            return movieController
        }
        else if viewController.isKindOfClass(BookController) {
            return musicController
        }
        return nil
    }
}

接著回到viewDidLoad()方法,在它的最下方,添加兩行代碼,代碼的作用可以從注釋中看出

//設置pageViewController的數據源代理為當前Controller
pageViewController.dataSource = self
    
//手動為pageViewController提供提一個頁面
pageViewController.setViewControllers([movieController], direction: UIPageViewControllerNavigationDirection.Forward, animated: true, completion: nil)

運行程序看看有什么效果,可以看到,用鼠標或者用手指(真機調試)左右滑動屏幕,有翻書的效果。到這里已經成功一半了,不過翻書的效果不是這里想要的,回到storyboard,找到Page View Controller,修改它的一個屬性。


這里將Transition Style改為了scroll,這樣做之后,頁面之間的切換效果就變成水平滑動了。

是時候為電影,音樂,圖書這3個頁面填充點內容了

3.右鍵點擊DataModel分組,新建一個Swift File,把它命名為CommonDataModel,在這個文件聲明一個結構體,作為基本的數據模型

/**
 *  基本數據模型, 包含圖像地址和標題
 */
struct CommonDataModel {
    var imageURL: String!
    var title: String
    
    init(imageURL: String, title: String) {
        self.imageURL = imageURL
        self.title = title
    }
}

這個結構體很簡單,一個存儲圖像地址的變量imageURL,和一個存儲標題的變量title,還有一個構造方法

4.右鍵點擊DataModel分組,創建3個Swift File文件,分別命名為MovieResults,MusicResults,BookResults。打開MovieResults.swift文件,添加下面這段代碼

class MovieResults {
    
    //存儲電影信息的數組
    var movies = [CommonDataModel]()
    
    init(dicts: [NSDictionary]) {
        
        //遍歷字典數組,拿到圖像地址,標題信息
        //接著使用CommonDataModel創建一條電影記錄
        for dict in dicts {
            let imageURL = (dict["images"] as! NSDictionary)["large"] as! String
            let title = dict["title"] as! String
            
            let movie = CommonDataModel(imageURL: imageURL, title: title)
            movies.append(movie)
        }
    }
}

這是存儲電影結果的類,movies變量用來保存電影記錄,從豆瓣平臺返回的數據經過解析之后,電影信息就是一個字典數組,因此這里遍歷字典數組來創建電影記錄,并添加到movies數組中

5.存儲音樂結果和圖書結果的類,和MovieResults類似?,F在分別在MusicResults.swift和BookResults.swift文件中,添加下面的兩段代碼。

class MusicResults {
    var musics = [CommonDataModel]()
    
    init(dicts: [NSDictionary]) {
        
        for dict in dicts {
            let imageURL = dict["image"] as! String
            let title = dict["title"] as! String
            
            let music = CommonDataModel(imageURL: imageURL, title: title)
            musics.append(music)
        }
    }
}
class BookResults {
    var books = [CommonDataModel]()
    
    init(dicts: [NSDictionary]) {
        
        for dict in dicts {
            let imageURL = (dict["images"] as! NSDictionary)["large"] as! String
            let title = dict["title"] as! String
            
            let book = CommonDataModel(imageURL: imageURL, title: title)
            books.append(book)
        }
    }
    
}

6.接著打開MovieController.swift文件,聲明一個MovieResult類型的實例變量,添加在movieTableView outlet的下方

@IBOutlet var movieTableView: UITableView!
    
var movieResults: MovieResults?

7.在import UIKit的下方添加一個import語句

import SDWebImage

,這條語句引入了SDWebImage模塊,借助它可以方便的加載網絡圖片

8.為movieTableView注冊一個可重用的Cell,這個Cell就是前面用XIB文件中的樣子。把下面這段代碼添加到viewDidLoad()方法中

movieTableView.registerNib(UINib(nibName: "CommonCell", bundle: nil), forCellReuseIdentifier: "CommonCell")

9.實現UITableViewDataSource的方法,為movieTableView提供數據,在最后一個花括號的地方,添加下面這段代碼

extension MovieController: UITableViewDataSource {
    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        
        if let result = moviesResult {
            return result.movies.count
        }
        
        return 0
    }
    
    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        
        let movie = moviesResult!.movies[indexPath.row]
        
        let cell = tableView.dequeueReusableCellWithIdentifier("CommonCell", forIndexPath: indexPath) as! CommonCell
        cell.coverImageView.sd_setImageWithURL(NSURL(string: movie.imageURL))
        cell.titleLabel.text = movie.title
        
        return cell
    }
}
  • 上面的代碼比較容易看懂,如果moviesResult是nil的話,返回的行數為0,不是nil的話就返回movies記錄的數量。
  • 在func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell 方法中拿到對應的電影記錄,根據ReuseIdentifier從cell的重用池中獲取想要的Cell類型,這里是在前面創建的CommonCell。
  • 接著為cell填充圖片和標題

10.在viewDidLoad()方法中設置movieTableView的數據源代理為當前類

movieTableView.dataSource = self

11.到現在為止都還沒有去獲取豆瓣上的數據,接下來就去獲取豆瓣上的數據,看可不可以在movieTableView中顯示出來。在在viewDidLoad()方法的最下面添加下面這段代碼

GetDataFromDouBan.getData("https://api.douban.com/v2/movie/search", type: "subjects", keyword: "張藝謀") { (data) -> Void in
            self.movieResults = MovieResults(dicts: data)
            self.movieTableView.reloadData()
}

借助幫助類GetDataFromDouBan的靜態方法getData()去獲取電影數據

運行之后,可以看到和下面截圖類似的結果


11.看到這個說明離成功只有一步之遙了,只是cell的高度太小了,接著實現UITableViewDelegate中的方法,來調整cell的高度。在MovieController.swift文件的最下方,添加這段代碼

//MARK: - UITableViewDelegate
extension MovieController: UITableViewDelegate {
    func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
        return 150
    }
    
    func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
        tableView.deselectRowAtIndexPath(indexPath, animated: true)
    }
}

最后在回到viewDidLoad()方法,在movieTableView.dataSource = self的下面,添加一句代碼,設置movieTableView的代理為當前類

movieTableView.delegate = self

再運行一次看看效果,比上一次好多了


此時整個MovieController.swift代碼文件的內容,應該和下面的類似

import UIKit
import SDWebImage

class MovieController: UIViewController {
    
    @IBOutlet var movieTableView: UITableView!
    
    var movieResults: MovieResults?

    override func viewDidLoad() {
        super.viewDidLoad()

        movieTableView.registerNib(UINib(nibName: "CommonCell", bundle: nil), forCellReuseIdentifier: "CommonCell")
        movieTableView.dataSource = self
        movieTableView.delegate = self
        
        GetDataFromDouBan.getData("https://api.douban.com/v2/movie/search", type: "subjects", keyword: "張藝謀") { (data) -> Void in
            self.movieResults = MovieResults(dicts: data)
            self.movieTableView.reloadData()
        }
    }

}

//MARK: - UITableViewDataSource
extension MovieController: UITableViewDataSource {
    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        
        if let result = movieResults {
            return result.movies.count
        }
        
        return 0
    }
    
    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        
        let movie = movieResults!.movies[indexPath.row]
        
        let cell = tableView.dequeueReusableCellWithIdentifier("CommonCell", forIndexPath: indexPath) as! CommonCell
        cell.coverImageView.sd_setImageWithURL(NSURL(string: movie.imageURL))
        cell.titleLabel.text = movie.title
        
        return cell
    }
}

//MARK: - UITableViewDelegate
extension MovieController: UITableViewDelegate {
    func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
        return 150
    }
    
    func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
        tableView.deselectRowAtIndexPath(indexPath, animated: true)
    }
}

12.實現了一個頁面,接下來就好辦多了,因為剩下的兩個頁面幾乎一摸一樣,下面分別是MusicController.swift和BookController.swift代碼文件的內容

import UIKit

class MusicController: UIViewController {
    
    @IBOutlet var musicTableView: UITableView!
    
    var musicResults: MusicResults?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        musicTableView.registerNib(UINib(nibName: "CommonCell", bundle: nil), forCellReuseIdentifier: "CommonCell")
        musicTableView.dataSource = self
        musicTableView.delegate = self
        
        GetDataFromDouBan.getData("https://api.douban.com/v2/music/search", type: "musics", keyword: "梁靜茹") { (data) -> Void in
            self.musicResults = MusicResults(dicts: data)
            self.musicTableView.reloadData()
        }
        
    }
    
}

extension MusicController: UITableViewDataSource {
    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        
        if let result = musicResults {
            return result.musics.count
        }
        
        return 0
    }
    
    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        
        let music = musicResults!.musics[indexPath.row]
        
        let cell = tableView.dequeueReusableCellWithIdentifier("CommonCell", forIndexPath: indexPath) as! CommonCell
        cell.coverImageView.sd_setImageWithURL(NSURL(string: music.imageURL))
        cell.titleLabel.text = music.title
        
        return cell
    }
}

extension MusicController: UITableViewDelegate {
    func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
        return 150
    }
    
    func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
        tableView.deselectRowAtIndexPath(indexPath, animated: true)
    }
}
import UIKit

class BookController: UIViewController {
    
    @IBOutlet var bookTableView: UITableView!
    
    var bookResults: BookResults?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        bookTableView.registerNib(UINib(nibName: "CommonCell", bundle: nil), forCellReuseIdentifier: "CommonCell")
        bookTableView.dataSource = self
        bookTableView.delegate = self
        
        GetDataFromDouBan.getData("https://api.douban.com/v2/book/search", type: "books", keyword: "swift") { (data) -> Void in
            self.bookResults = BookResults(dicts: data)
            self.bookTableView.reloadData()
        }
    }
    
}

extension BookController: UITableViewDataSource {
    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        
        if let result = bookResults {
            return result.books.count
        }
        
        return 0
    }
    
    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        
        let music = bookResults!.books[indexPath.row]
        
        let cell = tableView.dequeueReusableCellWithIdentifier("CommonCell", forIndexPath: indexPath) as! CommonCell
        cell.coverImageView.sd_setImageWithURL(NSURL(string: music.imageURL))
        cell.titleLabel.text = music.title
        
        return cell
    }
}

extension BookController: UITableViewDelegate {
    func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
        return 150
    }
    
    func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
        tableView.deselectRowAtIndexPath(indexPath, animated: true)
    }
}

幾乎和MovieController.swift的代碼內容一摸一樣,只是請求數據的地方不同。運行后,左右滑動,能夠在電影,音樂,圖書頁面之間切換。


13.這個demo就快要完成了,最后只剩下裝點的當前頁面提示條了。找一張條形圖片來當作提示條,把它拖到圖片資源文件夾中,把圖片命名為slider


14.回到storyboard找到Main Controller,我們的主控制器,打開輔助視圖,為包含提示條的View生成對應的outlet,把它命名為sliderView


15.為3個button生成一個Action,這個3個button都會出發這個Action,因為button的tag屬性有不同的值,所以根據tag值,可以知道點擊了哪個button。選中電影button,按住control鍵,用鼠標左鍵拖向代碼視圖中,生成相應的action,把action命名為changeCurrentPage。完成后把另外兩個button也關聯到這個action



16.回到MainController.swift代碼文件中,把提示條添加到頁面中

  • 在viewDidLoad()方法的上方添加一個變量,引用提示條圖片
var sliderImageView: UIImageView!
  • 在viewDidLoad()方法中的最下方,添加下面這段代碼,把提示條添加到頁面上
//添加提示條到頁面中

sliderImageView = UIImageView(frame: CGRect(x: 0, y: -1, width: self.view.frame.width / 3.0, height: 3.0))
sliderImageView.image = UIImage(named: "slider")
sliderView.addSubview(sliderImageView)

運行可以看到,提示條已經成功添加上去了
![](http://upload-images.jianshu.io/upload_images/706606-e8d5cdcddb76911f.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

17.為了能夠正確的使用button切換不同的頁面,以及同步提示條的位置,需要兩個變量來記錄當前頁面和上一個頁面的索引,還要一個包含3個頁面的數組

在bookController實例變量的下方聲明一個數組變量

var bookController: BookController!
var controllers = [UIViewController]()   //添加的代碼

在viewDidLoad()方法中把3個頁面添加到數組中

//把頁面添加到數組中
controllers.append(movieController)
controllers.append(musicController)
controllers.append(bookController)

接著在viewDidLoad()方法的上方添加兩個變量,索引當前頁面和上一個頁面

var lastPage = 0
var currentPage: Int = 0 {
    didSet {
        //根據當前頁面計算得到便宜量
        //一個微小的動畫移動提示條
        let offset = self.view.frame.width / 3.0 * CGFloat(currentPage)
        UIView.animateWithDuration(0.3) { () -> Void in
            self.sliderImageView.frame.origin = CGPoint(x: offset, y: -1)
        }
        
        //根據currentPage 和 lastPage的大小關系,控制頁面的切換方向
        if currentPage > lastPage {
            self.pageViewController.setViewControllers([controllers[currentPage]], direction: .Forward, animated: true, completion: nil)
        }
        else {
            self.pageViewController.setViewControllers([controllers[currentPage]], direction: .Reverse, animated: true, completion: nil)
        }
        
        lastPage = currentPage
    }
}

currentPage有一個屬性觀察器,當currentPage的值被設置之后,改變提示條的位置,并根據currentPage 和 lastPage的大小關系,設置頁面的切換方向(前進,后退)

18.設置currentPage的值,找到changeCurrentPage(sender: UIButton) action,在里面添加一句代碼

//button的tag分別為100,101,102,減去100之后就對應頁面的索引
currentPage = sender.tag - 100

這時運行一遍看看,點擊不同的按鈕可以切換到不同的頁面,提示條也會跟著移動


此時還有一個問題,當滑動屏幕進行頁面切換時,提示條沒有跟著移動。

19,解決最后的問題------使用通知

打開MovieController.swift代碼文件,在viewDidLoad()方法的下方,添加一個方法,這個方法會在頁面出現時調用,在這個方法中發送一個頁面改變的通知

override func viewWillAppear(animated: Bool) {
    super.viewWillAppear(animated)
    
   //發送一個名字為currentPageChanged,附帶object的值代表當前頁面的索引
   NSNotificationCenter.defaultCenter().postNotificationName("currentPageChanged", object: 0)
}

在MusicController.swift和BookController.swift代碼文件中添加相同的代碼,不過把object的值分別改為1和2

20.回到MainController.swift代碼文件,讓MainController接收頁面改變的通知,并對頁面改變作出響應。把下面的代碼添加viewDidLoad()方法中

//接收頁面改變的通知
NSNotificationCenter.defaultCenter().addObserver(self, selector: "currentPageChanged:", name: "currentPageChanged", object: nil)

在接收到通知之后,會調用currentPageChanged(notification: NSNotification)方法,接下來,添加這個方法,在這個方法中設置currentPage的值

//通知響應方法
func currentPageChanged(notification: NSNotification) {
    currentPage = notification.object as! Int
}

運行可以看到,滑動屏幕進行頁面切換,提示條也會跟著移動

到這里這個demo就結束了,不是很難_ !!


總結

在這個demo中可以看到,多個水平層次的頁面能夠用UIPageViewController方便的管理。雖然這里的頁面的布局和邏輯代碼都幾乎一樣,可是卻是各自獨立的,這意味著,每個頁面可以有不同的布局方式,不同的邏輯控制,而UIPageViewController僅僅是控制頁面的切換,不會對頁面自身造成影響。

完整demo地址

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容