【译】如何使用Storyboard创建UIPageViewController

声明:本文翻译自AppCoda网站的文章:How To Create UIPageViewController Using Storyboard,如有异议,请联系博主。
之前我们已经讲过UIPageViewController,那篇文章演示了如何使用Interface Builder创建UIPageViewController。为了适配iOS7和Xcode5,我们重新写了这篇新教程——使用Storyboard创建UIPageViewController。

你第一次打开一个app的时候,一定会发现一系列的引导页(或者是操作指南)来给你介绍一些这个app的特性,这是给用户介绍app功能的通常做法。这篇文章中,我们就来看看如何用UIPageViewController创建一个类似的引导页面。

UIPageViewController在iOS 5 SDK中首次引入,它使得开发者可以使用这个ViewController创建分页视图。在iOS 6中,这个类有了更新,支持滚动过渡效果。使用Page View,用户可以方便的通过手势在多个页面之间导航。UIPageViewController并不仅仅用于引导页,很多游戏,例如:愤怒的小鸟,就是用Page View来展示关卡选择的页面,还有有关书籍的应用,用这个类来显示书的页面。

[caption id=”” align=”aligncenter” width=”800” class=” “]example example[/caption]

UIPageViewController是个高度可配置的类,你可以进行如下配置:

  • 分页的方向——水平或垂直
  • 翻页的样式——书卷翻页或者滑动翻页
  • 书脊位置——只有书卷翻页样式有效
  • 页面间距——只有滑动翻页样式有效,用来定义页面间距(inter-page spacing)
    为了演示,我们会一起创建一个简单的app。当然,我们不会演示所有的UIPageViewController的配置细节,我们会演示到使用滑动翻页样式来创建一个引导页。不过别担心,有了对UIPageViewController的基本理解,我相信你能够去探索其他的特性。

开始吧!

Demo一览

我们要创建的Demo很简单,它会显示4个页面来介绍app的UI。用户可以在页面之间滑动切换。在任何时候用户点击“Start again”按钮会回到第一页。你能在Snapguide或Airbnb等app中找到很多类似的引导页,所以这个效果你应该不陌生。

Demo Glance

创建项目

打开Xcode并创建一个Simple View Application项目。选择Single View Application看起来有点奇怪,因为Xcode已经提供了基于UIPageViewController的具有完整功能的Page-Based Application模板。但是,这个模板还是有一些复杂,把这个模板解释清楚比重新开始一个项目要复杂的多。况且,从零开始一定会让我们对UIPageViewController的使用有更好的掌握。

好了,开始吧。下个页面中输入PageViewDemo作为项目名称,在company identifier栏中填入com.appcoda,设备类型选择iPhone。点击下一步并创建项目。

在Storyboard中创建Page View Controller

下一步,选择Main.storyboard。通常,你会看到一个默认的由Xcode生成的View Controller,先别管它,从Object Library拖出一个Page View Controller到Storyboard中。然后再拖出另一个View Controller。

在这个项目中,第一个View Controller会作为根View Controller,承载Page View Controller。最后添加的View Controller会作为页面的内容。后文中,用“根VC”代表“第一个View Controller”,“内容VC”代表“最后添加的View Controller”。

你可能会疑惑为什么只添加1个View Controller作为4页的内容,难道不应该使用4个View Controller吗?通过后面的演示你会发现,引导页都非常相似,通过复用这个View Controller显然是更好的选择。

下一步,给内容VC和Page View Controller分别设置一个ID。你能在Identity Inspector面板方便地设置。将Page View Controller的ID设置为“PageViewController”,将内容VC的ID设置为“PageContentController”。后面我们会在代码中使用到这些ID。

Page View Controller的默认变换样式是翻页效果(Page Curl),这个效果比较适合书籍类应用。引导页中,使用滑动效果更合适,所以将transition style更改为Scroll。

现在来设计内容vc的界面。拖出一个Image View和一个Label到Controller中。按照喜好更改字体和字号。但是你的View Controller应该和下面截图的样子类似。

对于那个默认的View Controller,在底部添加一个“Start again”按钮。

编写View Controller类

下一步是编写View Controller类并和对应的View Controller绑定。在菜单栏中选择File -> New -> File… 之后选择“Objective-C class”模板。命名为PageContentViewController,并使这个类继承自UIViewController。

返回Storyboard,选择内容VC并在Identify Inspector中设置Custom Class项为PageContentViewController。

下一步,我们为Image View和Label创建outlet。切换到(辅助编辑)Assistant Editor模式并确保PageContentViewController.h文件已经打开。按住Control键从Image View拖动到PageContentViewController.h创建IBOutlet。设置名字分别为backgroundImageView和titleLabel。

经过上面的编辑,PageContentViewController.h文件应该像下面这样:

1
2
3
4
5
6
7
#import <UIKit/UIKit.h>

@interface PageContentViewController : UIViewController
@property (weak, nonatomic) IBOutlet UIImageView *backgroundImageView;
@property (weak, nonatomic) IBOutlet UILabel *titleLabel;

@end

下一步,选择根VC并确保ViewController.h已经打开了。创建从“Start again”按钮的action,命名为“startWalkthrough”。

好了,我们已经完成了UI的设计,并已经创建好了outlet,开始实现View Controller类吧。

实现Page Content View Controller

实现PageContentViewController非常简单第一步,像PageContentViewController.h中添加以下属性:

1
2
3
@property NSUInteger pageIndex;
@property NSString *titleText;
@property NSString *imageFile;

pageIndex属性用来储存当前页面的索引(位置)。因为这个View Controller用来显示一张图片和一个标题,所以我们创建两个属性分别用来接收titleText和imageFile。下一步,打开PageContentViewController.m,将viewDidLoad:方法修改成这样:

1
2
3
4
5
6
7
- (void)viewDidLoad
{
[super viewDidLoad];

self.backgroundImageView.image = [UIImage imageNamed:self.imageFile];
self.titleLabel.text = self.titleText;
}

实现Page View Controller

UIPageViewController的定位是Controller的容器。这个容器用来包含和管理要在app中显示的多个View Controller,同时,控制一个View Controller切换到另一个View Controller的方式。这里的UIPageViewController就是让用户可以在页面间导航的容器,每个页面都由对应的View Controller对象管理。下面的插图说明了Page View Controller和内容VC之间的关系。

在UIPageViewController开始工作之前,我们必须实现UIPageViewControllerDataSource协议。PageViewController的Data Source负责按照(PageViewController的)需要提供内容VC。我们通过实现Data Source协议告知PageViewController每个页面显示什么内容。

在这个例子中,我们使用ViewController类作为UIPageViewController的Data Source。因此需要声明在ViewController类中声明它实现了UIPageViewControllerDateSource协议。

ViewController类还负责向内容VC提高数据(图片和标题)。打开ViewController.h,修改@interface声明,添加一个新的属性来保存UIPageViewController,同样的,创建图片和标题的属性。

1
2
3
4
5
6
7
8
9
10
11
#import <UIKit/UIKit.h>
#import "PageContentViewController.h"

@interface ViewController : UIViewController &lt;UIPageViewControllerDataSource&gt;

- (IBAction)startWalkthrough:(id)sender;
@property (strong, nonatomic) UIPageViewController *pageViewController;
@property (strong, nonatomic) NSArray *pageTitles;
@property (strong, nonatomic) NSArray *pageImages;

@end

在ViewController.m文件中,在viewDidLoad:方法中初始化标题和图片:

1
2
3
4
5
6
7
- (void)viewDidLoad
{
[super viewDidLoad];

_pageTitles = @[@"Over 200 Tips and Tricks", @"Discover Hidden Features", @"Bookmark Favorite Tip", @"Free Regular Update"];
_pageImages = @[@"page1.png", @"page2.png", @"page3.png", @"page4.png"];
}

注意:你能在这个链接下载这些图片并添加到你的Xcode项目中。
我们已经创建好了页面内容的数据模型。下一步,我们要至少实现UIPageViewControllerDataSource的以下两个方法:

  • viewControllerAfterViewController - 提供当前View Controller的后一个View Controller。换言之,我们告诉app下一页显示什么内容。
  • viewControllerBeforeViewController - 提供当前View Controller的前一个View Controller。换言之,我们告诉app上一页显示什么内容。

在ViewController.m文件的@end之前,添加如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#pragma mark - Page View Controller Data Source

- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController
{
NSUInteger index = ((PageContentViewController*) viewController).pageIndex;

if ((index == 0) || (index == NSNotFound)) {
return nil;
}

index--;
return [self viewControllerAtIndex:index];
}

- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController
{
NSUInteger index = ((PageContentViewController*) viewController).pageIndex;

if (index == NSNotFound) {
return nil;
}

index++;
if (index == [self.pageTitles count]) {
return nil;
}
return [self viewControllerAtIndex:index];
}

上面的方法非常简单。首先,我们获取当前的页码,简单地增加/减少页码并返回要显示的View Controller。当然,我们需要做边缘检查,并在超出时返回nil。

正如你注意到的那样,我们还没有创建viewControllerAtIndex:方法。这是一个用来创建所需位置的内容VC的辅助方法。它接收一个index参数并创建对应的内容VC。

在ViewController.m文件中,添加这个辅助方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
- (PageContentViewController *)viewControllerAtIndex:(NSUInteger)index
{
if (([self.pageTitles count] == 0) || (index &gt;= [self.pageTitles count])) {
return nil;
}

// Create a new view controller and pass suitable data.
PageContentViewController *pageContentViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"PageContentViewController"];
pageContentViewController.imageFile = self.pageImages[index];
pageContentViewController.titleText = self.pageTitles[index];
pageContentViewController.pageIndex = index;

return pageContentViewController;
}

还记得吗,我们在设计UI时为这些View Controller设置了ID。这个ID用于创建View Controller对象时的引用。为了实例化一个View Controller对象,你需要使用instantiateViewControllerWithIdentifier:方法,并传入一个ID。

1
PageContentViewController *pageContentViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"PageContentViewController"];

为了显示页面指示器(Page Indicator),你需要告诉iOS Page View Controller要显示的页面的数量(也就是点的数量),以及初始选择那一页。在ViewController.m的底部添加以下两个方法:

1
2
3
4
5
6
7
8
9
10

\- (NSInteger)presentationCountForPageViewController:(UIPageViewController *)pageViewController
{
return [self.pageTitles count];
}

\- (NSInteger)presentationIndexForPageViewController:(UIPageViewController *)pageViewController
{
return 0;
}

同样的,上面两个方法也非常简单。我们仅仅是告诉iOS我们一共需要显示多少页面,以及默认选择的第一页。

注意:你必须实现上面的方法才能显示页面指示器(Page Indicator)。而且,页面指示器只在滑动切换模式下起作用。

初始化UIPageViewController

最后一步就是创建并初始化UIPageViewController,最好的位置就是viewDidLoad方法。打开ViewController.m文件,修改viewDidLoad:方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
- (void)viewDidLoad
{
[super viewDidLoad];
// Create the data model
_pageTitles = @[@"Over 200 Tips and Tricks", @"Discover Hidden Features", @"Bookmark Favorite Tip", @"Free Regular Update"];
_pageImages = @[@"page1.png", @"page2.png", @"page3.png", @"page4.png"];

// Create page view controller
self.pageViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"PageViewController"];
self.pageViewController.dataSource = self;

PageContentViewController *startingViewController = [self viewControllerAtIndex:0];
NSArray *viewControllers = @[startingViewController];
[self.pageViewController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:nil];

// Change the size of page view controller
self.pageViewController.view.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height - 30);

[self addChildViewController:_pageViewController];
[self.view addSubview:_pageViewController.view];
[self.pageViewController didMoveToParentViewController:self];

}

让我们看一下这个方法做了什么。首先我们创建了PageViewController实例。随后我们制定了Data Source,在这个例子中就是这个类本身。然后我们创建了第一个内容VC并把它添加到一个Controller数组中,并且将它和要显示的ViewController关联。

最后,我们修改内容VC的大小并将内容VC的view添加到当前view中。

自定义Page Indicator

如果你现在编译并运行这个app,它会正常运行但是你可能会发现页面指示器并没有显示。事实上页面指示器已经显示了但是点的颜色和view的颜色一样,所以让我们来修改它的颜色吧。

在AppDelegate.m文件中,在didFinishLaunchingWithOptions:方法中添加下面几行代码:

1
2
3
4
UIPageControl *pageControl = [UIPageControl appearance];
pageControl.pageIndicatorTintColor = [UIColor lightGrayColor];
pageControl.currentPageIndicatorTintColor = [UIColor blackColor];
pageControl.backgroundColor = [UIColor whiteColor];

编译并运行这个app

好了,启动这个应用然后看UIPageViewController运行得如何。你应该可以在iPhone模拟器中加载Page View Controller了。尝试在多个页面之间滑动一下。

返回首页

还有一件事没做。“Start again”按钮还没有实现功能。点击的时候,我们希望Page View Controller滑动到第一页。你可以使用UIPageViewController的setViewControllers:方法切换页面。为了回到第一页,修改ViewController.m的startWalkthrough:方法:

1
2
3
4
5
- (IBAction)startWalkthrough:(id)sender {
PageContentViewController *startingViewController = [self viewControllerAtIndex:0];
NSArray *viewControllers = @[startingViewController];
[self.pageViewController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionReverse animated:NO completion:nil];
}

再次运行app。点击“Start again”按钮会将你带回第一页。

总结

这篇教程中,我们介绍了UIPageViewController的基础知识并演示了如何使用Storyboard创建Controller。UIPageViewController是实现引导页的非常方便的类。同时,UIPageViewController的适用范围非常广阔,你可以用它来显示任意你想显示的信息,比如:多个网页。UIPageViewController还有丰富的配置项。这篇教程只涵盖了滑动样式,但是可别忘了你可以使用这个类简单地创建一个读书应用呢!只需要将滑动样式更改为翻页样式就可以了。所以别满足于这个小例子,尝试修改并了解UIPageViewController的其他选项吧。

如果需要完整参考,可以在这个链接下载Xcode项目。和往常一样,请将你的评论和想法告知我们

Demo项目已本地到博主VPS,放心下载。

声明:本文翻译自AppCoda网站的文章:How To Create UIPageViewController Using Storyboard,如有异议,请联系博主。