使用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截图



还差一点点,布局工作就要完成了,加油!!!!
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类似。现在分别在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地址

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容