美文网首页固予
UIPageViewControlller详解

UIPageViewControlller详解

作者: ashura_ | 来源:发表于2017-05-23 20:05 被阅读131次

简介

UIPageViewController 可以实现内容页之间的导航功能,每一页的内容都由它自己的view controller来管理

主要的展示方式为以下七种

黄建武的_iPhone_实时桌面.png

下边以过渡方式来说明UIPageViewController的用法。

页面过渡有两种方式

typedef NS_ENUM(NSInteger, UIPageViewControllerTransitionStyle) {
    UIPageViewControllerTransitionStylePageCurl = 0, // Navigate between views via a page curl transition.
    UIPageViewControllerTransitionStyleScroll = 1 // Navigate between views by scrolling.
};
  • UIPageViewControllerTransitionStyle.PageCurl(翻书效果)
  • UIPageViewControllerTransitionStyle.Scroll(滚动效果)

PageCurl

重要概念

双面显示(doubleSided)

The default value for this property is NO.

If the back of pages has no content (the value is NO), then the content on the front of the page will partially show through to the back when turning pages.

If the spine is located in the middle, the value must be YES. Setting it to NO with the spine located in the middle raises an exception.

默认doubleSidedNO,如果参数是UIPageViewControllerSpineLocationMid则必须是YES,不然就崩溃。
看效果:

  1. 书脊UIPageViewControllerSpineLocationMin参数doubleSidedNO
021.gif
  1. 书脊UIPageViewControllerSpineLocationMin参数doubleSidedYES
022.gif
  1. 书脊UIPageViewControllerSpineLocationMid
023.gif
  1. 书脊UIPageViewControllerSpineLocationMaX
024.gif

当书脊位置在左边时候,doubleSidedYES时候,下一个界面当做翻页时候过渡界面了

书脊位置(spineLocation)

typedef NS_ENUM(NSInteger, UIPageViewControllerSpineLocation) {
    UIPageViewControllerSpineLocationNone = 0, // Returned if 'spineLocation' is queried when 'transitionStyle' is not 'UIPageViewControllerTransitionStylePageCurl'.
    UIPageViewControllerSpineLocationMin = 1,  // Requires one view controller.
    UIPageViewControllerSpineLocationMid = 2,  // Requires two view controllers.
    UIPageViewControllerSpineLocationMax = 3   // Requires one view controller.
};   // Only pertains to 'UIPageViewControllerTransitionStylePageCurl'.

上边表达的意思是书脊位置只在UIPageViewControllerTransitionStylePageCurl方式有效,并且影响下边Select的参数viewControllers的内容。参数是UIPageViewControllerSpineLocationMid时候,要求有2个ViewController,其他情况只能传一个。

- (void)setViewControllers:(nullable NSArray<UIViewController *> *)viewControllers direction:(UIPageViewControllerNavigationDirection)direction animated:(BOOL)animated completion:(void (^ __nullable)(BOOL finished))completion
Spline location Double sided whate to pass
.Mid(中间位置) true 传要显示的左边页面和右边页面
.Min .Max true 传将要显示页面的前页面和后页面,后页面用于动画
.Min or .Max false 传将要显示页面的前面页面

注意:按照官方文档的表格,当书脊为Min/Max,而ine locationYES时候,是这么说的

Pass the front of the page to be displayed and the back of the previously-displayed page. The back is used for the page turning animation.

事实却是只需用传递一个参数就可以了,并且枚举UIPageViewControllerSpineLocation.h解释也是只有Mid才会传2个参数。这块理解费劲,但左书脊带双边代码是一个controller而不是两个。

        UIViewController *currentViewController = self.pageViewController.viewControllers[0];
        NSArray *viewControllers = @[currentViewController];
        [self.pageViewController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:YES completion:nil];
        
        self.pageViewController.doubleSided = self.doubleSided;

        return self.location;

使用方法

初始化

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    // Configure the page view controller and add it as a child view controller.
    self.pageViewController = [[UIPageViewController alloc] initWithTransitionStyle:UIPageViewControllerTransitionStylePageCurl navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal options:nil];
    self.pageViewController.delegate = self;
    
    DataViewController *startingViewController = [self.dataSource viewControllerAtIndex:5];
    NSArray *viewControllers = @[startingViewController];
    [self.pageViewController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:nil];
    
    self.pageViewController.dataSource = self.dataSource;
    
    [self addChildViewController:self.pageViewController];
    [self.view addSubview:self.pageViewController.view]; 
    [self.pageViewController didMoveToParentViewController:self];
}

注意Controller添加子Controller的方式,这是另外一个技术点。

实现Datasource

Datasource封装置后通用,核心实现以下两个selector即可。viewControllerBeforeViewController范湖前一个ViewController,viewControllerAfterViewController返回后一个viewControllerAfterViewController

- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController
{
    NSUInteger index = [self indexOfViewController:(DataViewController *)viewController];
    if ((index == 0) || (index == NSNotFound)) {
        return nil;
    }
    
    index--;
    return [self viewControllerAtIndex:index];
}

- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController
{
    NSUInteger index = [self indexOfViewController:(DataViewController *)viewController];
    if (index == NSNotFound) {
        return nil;
    }
    
    index++;
    if (index == [self.pageData count]) {
        return nil;
    }
    return [self viewControllerAtIndex:index ];
}

实现Delegate

selector- setViewControllers:direction:animated:completion:调用之后执行,书脊不在中间传递当前Controller,在中间根据当前Controller是奇数还是偶数,来传递的是前一个Controller+当前Controller,还是当前Controller+后一个Controller

原理
参照横屏书脊Mid可以看出一月二月是这本书显示出的当前界面,是左边一月Controller+右边二月Controller

如果当前Controller一月则是奇数,要传递当前一月+下一个月二月,如果是偶数二月,则要传递前一个月一月+当前月二月

 - (UIPageViewControllerSpineLocation)pageViewController:(UIPageViewController *)pageViewController spineLocationForInterfaceOrientation:(UIInterfaceOrientation)orientation {
//    BOOL b = UIInterfaceOrientationIsPortrait(orientation);
//    UIUserInterfaceIdiom idiom = [[UIDevice currentDevice] userInterfaceIdiom];
    if (UIInterfaceOrientationIsPortrait(orientation)  && self.location != UIPageViewControllerSpineLocationMid/*|| ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone)*/) {
        // In portrait orientation or on iPhone: Set the spine position to "min" and the page view controller's view controllers array to contain just one view controller. Setting the spine position to 'UIPageViewControllerSpineLocationMid' in landscape orientation sets the doubleSided property to YES, so set it to NO here.
        
        UIViewController *currentViewController = self.pageViewController.viewControllers[0];
        NSArray *viewControllers = @[currentViewController];
        [self.pageViewController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:YES completion:nil];
        
        self.pageViewController.doubleSided = self.doubleSided;

        return self.location;
    }
    
    // In landscape orientation: Set set the spine location to "mid" and the page view controller's view controllers array to contain two view controllers. If the current page is even, set it to contain the current and next view controllers; if it is odd, set the array to contain the previous and current view controllers.
    DataViewController *currentViewController = self.pageViewController.viewControllers[0];
    NSArray *viewControllers = nil;
    
    NSUInteger indexOfCurrentViewController = [self.dataSource indexOfViewController:currentViewController];
    if (indexOfCurrentViewController == 0 || indexOfCurrentViewController % 2 == 0) {
        UIViewController *nextViewController = [self.dataSource pageViewController:self.pageViewController viewControllerAfterViewController:currentViewController];
        viewControllers = @[currentViewController, nextViewController];
    } else {
        UIViewController *previousViewController = [self.dataSource pageViewController:self.pageViewController viewControllerBeforeViewController:currentViewController];
        viewControllers = @[previousViewController, currentViewController];
    }
    [self.pageViewController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:YES completion:nil];
    
    self.pageViewController.doubleSided = YES;
    
    return UIPageViewControllerSpineLocationMid;
}

Scroll

重要概念

滚动方向

滚动方向只在Scroll过渡方式有效,在PageCurl方式指定成任何一个效果都是一样的。

typedef NS_ENUM(NSInteger, UIPageViewControllerNavigationDirection) {
    UIPageViewControllerNavigationDirectionForward,
    UIPageViewControllerNavigationDirectionReverse
};  // For 'UIPageViewControllerNavigationOrientationHorizontal', 'forward' is right-to-left, like pages in a book. For 'UIPageViewControllerNavigationOrientationVertical', bottom-to-top, like pages in a wall calendar.

InterPageSpacing

滚动Controller的间隔

  • 间隔为0
025.gif
  • 间隔为20
026.gif

间隔参数在UIPageViewController初始化时候提供

NSDictionary *dic = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:UIPageViewControllerSpineLocationNone],UIPageViewControllerOptionSpineLocationKey,[NSNumber numberWithFloat:self.spacing],UIPageViewControllerOptionInterPageSpacingKey, nil];
self.pageViewController = [[UIPageViewController alloc] initWithTransitionStyle:UIPageViewControllerTransitionStyleScroll navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal options:dic];

PageControl

027.gif
要显示出PageControl只要presentationCountForPageViewController的返回值大于0则自动显示。
- (NSInteger)presentationCountForPageViewController:(UIPageViewController *)pageViewController
{
    if(_showPageControl)
    {
    return [self.pageData count];
    }
    return 0;
    
}
- (NSInteger)presentationIndexForPageViewController:(UIPageViewController *)pageViewController
{
    return _currentIndex;
}

presentationIndexForPageViewController:具体要返回什么?

Return Value
The number of items to be reflected in the page indicator.

绝大部分Demo里都是直接返回0,官方文档解释也不太容易理解,其实就是返回当前的Index.这里的值取决于初始化时候传递的参数。
setViewControllers:direction:animated:completion:会自动调用presentationCountForPageViewController:以及presentationIndexForPageViewController

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

上边代码里传入Index为2,则对应的presentationIndexForPageViewController:返回2,pageControl才能对应上,不然会错乱。

代码

稍后上传

相关文章

网友评论

    本文标题:UIPageViewControlller详解

    本文链接:https://www.haomeiwen.com/subject/kvhbxxtx.html