Using UIPageControl as a container UIViewController

虽然看上去用 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  
  2.     
  3. @property (nonatomic, strong) IBOutlet UIScrollView *scrollView;  
  4. @property (nonatomic, strong) IBOutlet UIPageControl *pageControl;  
  5.     
  6. - (IBAction)changePage:(id)sender;  
  7.  @end  
在实现的代码文件中,首先我先不让它能够旋转屏幕。然后当 PagerViewController 预备显示时,我们需要标记一下当前哪一个view是被激活的,即被显示的,那么需要在 viewDidApplear 以及 viewWillDisappear方法中加入以下代码:
  1. - (BOOL)automaticallyForwardAppearanceAndRotationMethodsToChildViewControllers {  
  2.     returnNO;  
  3. }  
  4.     
  5. - (void)viewDidAppear:(BOOL)animated {  
  6.     [super viewDidAppear:animated];  
  7.     UIViewController *viewController = [self.childViewControllers objectAtIndex:self.pageControl.currentPage];  
  8.     if(viewController.view.superview != nil) {  
  9.         [viewController viewDidAppear:animated];  
  10.     }  
  11. }  
  12.     
  13. - (void)viewWillDisappear:(BOOL)animated {  
  14.     UIViewController *viewController = [self.childViewControllers objectAtIndex:self.pageControl.currentPage];  
  15.     if(viewController.view.superview != nil) {  
  16.         [viewController viewWillDisappear:animated];  
  17.     }  
  18.     [super viewWillDisappear:animated];  
  19. }  
  20.     
  21. - (void)viewDidDisappear:(BOOL)animated {  
  22.     UIViewController *viewController = [self.childViewControllers objectAtIndex:self.pageControl.currentPage];  
  23.     if(viewController.view.superview != nil) {  
  24.         [viewController viewDidDisappear:animated];  
  25.     }  
  26.     [super viewDidDisappear:animated];  
  27. }  
viewWillAppear 可能会有点复杂,因为我们也需要加载所有的子view到 scrollView中,而且必须确保scrollview的contentsize比子view要大。
  1. - (void)viewWillAppear:(BOOL)animated {  
  2.     [super viewWillAppear:animated];  
  3.     
  4.     for(NSUInteger i =0; i < [self.childViewControllers count]; i++) {  
  5.         [self loadScrollViewWithPage:i];  
  6.     }  
  7.     
  8.     self.pageControl.currentPage = 0;  
  9.     _page = 0;  
  10.     [self.pageControl setNumberOfPages:[self.childViewControllers count]];  
  11.     
  12.     UIViewController *viewController = [self.childViewControllers objectAtIndex:self.pageControl.currentPage];  
  13.     if(viewController.view.superview != nil) {  
  14.         [viewController viewWillAppear:animated];  
  15.     }  
  16.     
  17.     self.scrollView.contentSize = CGSizeMake(scrollView.frame.size.width * [self.childViewControllers count], scrollView.frame.size.height);  
  18. }  
为了将UIViewController的内容加载到UIScrolView的contentView中,需要如下代码:
  1. - (void)loadScrollViewWithPage:(int)page {  
  2.     if(page < 0)  
  3.         return;  
  4.     if(page >= [self.childViewControllers count])  
  5.         return;  
  6.     
  7.     // replace the placeholder if necessary  
  8.     UIViewController *controller = [self.childViewControllers objectAtIndex:page];  
  9.     if(controller == nil) {  
  10.         return;  
  11.     }  
  12.     
  13.     // add the controller's view to the scroll view  
  14.     if(controller.view.superview == nil) {  
  15.         CGRect frame = self.scrollView.frame;  
  16.         frame.origin.x = frame.size.width * page;  
  17.         frame.origin.y = 0;  
  18.         controller.view.frame = frame;  
  19.         [self.scrollView addSubview:controller.view];  
  20.     }  
  21. }  
处理滚动
为了处理滚动,我们需要实现几个 UIScrollViewDelegate 的方法以及尽早声明方法 - (IBAction)changePage:(id)sender

首先必须先知道滚动是如何实现的,要么是手势,要么就是点击了 UIPageControl 的一侧。

来一起看看下面的代码:
  1. // At the begin of scroll dragging, reset the boolean used when scrolls originate from the UIPageControl  
  2. - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {  
  3.          _pageControlUsed = NO;  
  4. }  
  5.     
  6. // At the end of scroll animation, reset the boolean used when scrolls originate from the UIPageControl  
  7. - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {  
  8.          _pageControlUsed = NO;  
  9. }  
  10.     
  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];  
  16.     
  17.         _page = self.pageControl.currentPage;  
  18. }  
现在,为了当页面改变时也能更新当前的显示,我们只需要实现 scrollViewDidScroll delegate方法以及 changePage IBAction 方法。
  1. - (IBAction)changePage:(id)sender {  
  2.         intpage = ((UIPageControl *)sender).currentPage;  
  3.     
  4.         // update the scroll view to the appropriate page  
  5.         CGRect frame = self.scrollView.frame;  
  6.         frame.origin.x = frame.size.width * page;  
  7.         frame.origin.y = 0;  
  8.     
  9.         UIViewController *oldViewController = [self.childViewControllers objectAtIndex:_page];  
  10.         UIViewController *newViewController = [self.childViewControllers objectAtIndex:self.pageControl.currentPage];  
  11.         [oldViewController viewWillDisappear:YES];  
  12.         [newViewController viewWillAppear:YES];  
  13.     
  14.         [self.scrollView scrollRectToVisible:frame animated:YES];  
  15.     
  16.         // Set the boolean used when scrolls originate from the UIPageControl. See scrollViewDidScroll: above.  
  17.         _pageControlUsed = YES;  
  18. }  
  19.     
  20. - (void)scrollViewDidScroll:(UIScrollView *)sender {  
  21.     // We don't want a "feedback loop" between the UIPageControl and the scroll delegate in  
  22.     // which a scroll event generated from the user hitting the page control triggers updates from  
  23.     // the delegate method. We use a boolean to disable the delegate logic when the page control is used.  
  24.     if(_pageControlUsed || _rotating) {  
  25.         // do nothing - the scroll was initiated from the page control, not the user dragging  
  26.         return;  
  27.     }  
  28.     
  29.     // Switch the indicator when more than 50% of the previous/next page is visible  
  30.         CGFloat pageWidth = self.scrollView.frame.size.width;  
  31.         intpage = 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];  
  40.                 _page = page;  
  41.         }  
  42. }  
最后处理旋转

首先在shouldAutorotateToInterfaceOrientation方法中返回 YES,同时也需要传输一下3个消息给当前被显示的子 UIViewController。

  1. willAnimateRotationToInterfaceOrientation:duration:  
  2. willRotateToInterfaceOrientation:duration:  
  3.   
  4. didRotateFromInterfaceOrientation:  

但我们还是要自己处理旋转的问题,比如要重新缩放scrollview的contentView,以及调整子view的帧率,否则会出现一些无法预料的事情。所以来看看以下处理方式
  1. - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation  
  2. {  
  3.     returnYES;  
  4. }  
  5.     
  6. - (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {  
  7.     UIViewController *viewController = [self.childViewControllers objectAtIndex:self.pageControl.currentPage];  
  8.     [viewController willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];  
  9.     _rotating = YES;  
  10. }  
  11.     
  12. - (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {  
  13.     
  14.     UIViewController *viewController = [self.childViewControllers objectAtIndex:self.pageControl.currentPage];  
  15.     [viewController willAnimateRotationToInterfaceOrientation:toInterfaceOrientation duration:duration];  
  16.     
  17.     self.scrollView.contentSize = CGSizeMake(scrollView.frame.size.width * [self.childViewControllers count], scrollView.frame.size.height);  
  18.     NSUInteger page = 0;  
  19.     for(viewController in self.childViewControllers) {  
  20.         CGRect frame = self.scrollView.frame;  
  21.         frame.origin.x = frame.size.width * page;  
  22.         frame.origin.y = 0;  
  23.         viewController.view.frame = frame;  
  24.         page++;  
  25.     }  
  26.     
  27.     CGRect frame = self.scrollView.frame;  
  28.     frame.origin.x = frame.size.width * _page;  
  29.     frame.origin.y = 0;  
  30.     [self.scrollView scrollRectToVisible:frame animated:NO];  
  31.     
  32. }  
  33.     
  34. - (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {  
  35.     _rotating = NO;  
  36.     UIViewController *viewController = [self.childViewControllers objectAtIndex:self.pageControl.currentPage];  
  37.     [viewController didRotateFromInterfaceOrientation:fromInterfaceOrientation];  
  38. }  
总结
废话少说,这里放出代码,在GitHub
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值