虽然看上去用 UIPageControl 在一系列 UIView或UIViewController中导航是很平常的事情,但实际上
Apple公司并没有提供一个这样的方法或者演示Demo:
在最新的iOS版本中(5.0 现在已经不是最新的),Apple公司提供了很多如何用其他方式实现
UIViewController容器的方式(可以从这里参考),但悲剧的是它们与 UIPageControl 没多大关系。
那么我们今天就来专门解决这个问题。
创建界面
首先我们需要在 Interface Builder 中在一个UIViewController里面创建一个UIPageControl与UIScrollView。当然你可以创建很多类似的UIViewController。
设置 PageViewController 类
这里的 PageViewController 对象将包含一个 UIScrollView 与一个 UIPageControl。而且 UIScrollView 将会包含所有的 subview, 这些subview就是被UIPageControl控制的子页面。
你也许会在 viewController 中添加一些算法来专门处理页面转换: addChildViewController:方法。然而在我这个案例中却不是这么做的,来一起看看代码:
1 | @interface PagerViewController : UIViewController |
3 | @property (nonatomic, strong) IBOutlet UIScrollView *scrollView; |
4 | @property (nonatomic, strong) IBOutlet UIPageControl *pageControl; |
6 | - (IBAction)changePage:(id)sender; |
在实现的代码文件中,首先我先不让它能够旋转屏幕。然后当 PagerViewController 预备显示时,我们需要标记一下当前哪一个view是被激活的,即被显示的,那么需要在 viewDidApplear 以及 viewWillDisappear方法中加入以下代码:
01 | - ( BOOL )automaticallyForwardAppearanceAndRotationMethodsToChildViewControllers { |
05 | - ( void )viewDidAppear:( BOOL )animated { |
06 | [super viewDidAppear:animated]; |
07 | UIViewController *viewController = [self.childViewControllers objectAtIndex:self.pageControl.currentPage]; |
08 | if (viewController.view.superview != nil) { |
09 | [viewController viewDidAppear:animated]; |
13 | - ( void )viewWillDisappear:( BOOL )animated { |
14 | UIViewController *viewController = [self.childViewControllers objectAtIndex:self.pageControl.currentPage]; |
15 | if (viewController.view.superview != nil) { |
16 | [viewController viewWillDisappear:animated]; |
18 | [super viewWillDisappear:animated]; |
21 | - ( void )viewDidDisappear:( BOOL )animated { |
22 | UIViewController *viewController = [self.childViewControllers objectAtIndex:self.pageControl.currentPage]; |
23 | if (viewController.view.superview != nil) { |
24 | [viewController viewDidDisappear:animated]; |
26 | [super viewDidDisappear:animated]; |
viewWillAppear 可能会有点复杂,因为我们也需要加载所有的子view到 scrollView中,而且必须确保scrollview的contentsize比子view要大。
01 | - ( void )viewWillAppear:( BOOL )animated { |
02 | [super viewWillAppear:animated]; |
04 | for (NSUInteger i =0; i < [self.childViewControllers count]; i++) { |
05 | [self loadScrollViewWithPage:i]; |
08 | self.pageControl.currentPage = 0; |
10 | [self.pageControl setNumberOfPages:[self.childViewControllers count]]; |
12 | UIViewController *viewController = [self.childViewControllers objectAtIndex:self.pageControl.currentPage]; |
13 | if (viewController.view.superview != nil) { |
14 | [viewController viewWillAppear:animated]; |
17 | self.scrollView.contentSize = CGSizeMake(scrollView.frame.size.width * [self.childViewControllers count], scrollView.frame.size.height); |
为了将UIViewController的内容加载到UIScrolView的contentView中,需要如下代码:
01 | - ( void )loadScrollViewWithPage:( int )page { |
04 | if (page >= [self.childViewControllers count]) |
08 | UIViewController *controller = [self.childViewControllers objectAtIndex:page]; |
09 | if (controller == nil) { |
14 | if (controller.view.superview == nil) { |
15 | CGRect frame = self.scrollView.frame; |
16 | frame.origin.x = frame.size.width * page; |
18 | controller.view.frame = frame; |
19 | [self.scrollView addSubview:controller.view]; |
处理滚动
为了处理滚动,我们需要实现几个 UIScrollViewDelegate 的方法以及尽早声明方法 - (IBAction)changePage:(id)sender
首先必须先知道滚动是如何实现的,要么是手势,要么就是点击了 UIPageControl 的一侧。
来一起看看下面的代码:
02 | - ( void )scrollViewWillBeginDragging:(UIScrollView *)scrollView { |
03 | _pageControlUsed = NO; |
07 | - ( void )scrollViewDidEndDecelerating:(UIScrollView *)scrollView { |
08 | _pageControlUsed = NO; |
11 | - ( void )scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView { |
12 | UIViewController *oldViewController = [self.childViewControllers objectAtIndex:_page]; |
13 | UIViewController *newViewController = [self.childViewControllers objectAtIndex:self.pageControl.currentPage]; |
14 | [oldViewController viewDidDisappear:YES]; |
15 | [newViewController viewDidAppear:YES]; |
17 | _page = self.pageControl.currentPage; |
现在,为了当页面改变时也能更新当前的显示,我们只需要实现 scrollViewDidScroll delegate方法以及 changePage IBAction 方法。
01 | - (IBAction)changePage:(id)sender { |
02 | int page = ((UIPageControl *)sender).currentPage; |
05 | CGRect frame = self.scrollView.frame; |
06 | frame.origin.x = frame.size.width * page; |
09 | UIViewController *oldViewController = [self.childViewControllers objectAtIndex:_page]; |
10 | UIViewController *newViewController = [self.childViewControllers objectAtIndex:self.pageControl.currentPage]; |
11 | [oldViewController viewWillDisappear:YES]; |
12 | [newViewController viewWillAppear:YES]; |
14 | [self.scrollView scrollRectToVisible:frame animated:YES]; |
17 | _pageControlUsed = YES; |
20 | - ( void )scrollViewDidScroll:(UIScrollView *)sender { |
24 | if (_pageControlUsed || _rotating) { |
30 | CGFloat pageWidth = self.scrollView.frame.size.width; |
31 | int page = floor ((self.scrollView.contentOffset.x - pageWidth / 2) / pageWidth) + 1; |
32 | if (self.pageControl.currentPage != page) { |
33 | UIViewController *oldViewController = [self.childViewControllers objectAtIndex:self.pageControl.currentPage]; |
34 | UIViewController *newViewController = [self.childViewControllers objectAtIndex:page]; |
35 | [oldViewController viewWillDisappear:YES]; |
36 | [newViewController viewWillAppear:YES]; |
37 | self.pageControl.currentPage = page; |
38 | [oldViewController viewDidDisappear:YES]; |
39 | [newViewController viewDidAppear:YES]; |
最后处理旋转
首先在shouldAutorotateToInterfaceOrientation方法中返回 YES,同时也需要传输一下3个消息给当前被显示的子 UIViewController。
willAnimateRotationToInterfaceOrientation:duration:
willRotateToInterfaceOrientation:duration:
didRotateFromInterfaceOrientation:
但我们还是要自己处理旋转的问题,比如要重新缩放scrollview的contentView,以及调整子view的帧率,否则会出现一些无法预料的事情。所以来看看以下处理方式
01 | - ( BOOL )shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation |
06 | - ( void )willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration { |
07 | UIViewController *viewController = [self.childViewControllers objectAtIndex:self.pageControl.currentPage]; |
08 | [viewController willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration]; |
12 | - ( void )willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration { |
14 | UIViewController *viewController = [self.childViewControllers objectAtIndex:self.pageControl.currentPage]; |
15 | [viewController willAnimateRotationToInterfaceOrientation:toInterfaceOrientation duration:duration]; |
17 | self.scrollView.contentSize = CGSizeMake(scrollView.frame.size.width * [self.childViewControllers count], scrollView.frame.size.height); |
19 | for (viewController in self.childViewControllers) { |
20 | CGRect frame = self.scrollView.frame; |
21 | frame.origin.x = frame.size.width * page; |
23 | viewController.view.frame = frame; |
27 | CGRect frame = self.scrollView.frame; |
28 | frame.origin.x = frame.size.width * _page; |
30 | [self.scrollView scrollRectToVisible:frame animated:NO]; |
34 | - ( void )didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation { |
36 | UIViewController *viewController = [self.childViewControllers objectAtIndex:self.pageControl.currentPage]; |
37 | [viewController didRotateFromInterfaceOrientation:fromInterfaceOrientation]; |
总结