在UIPageViewController中,是否可以通过编程方式翻页?

169

UIPageViewController 中,是否可以通过编程的方式翻页?


完整的解释在这里:https://dev59.com/eWMl5IYBdhLWcg3wcmrD#26024779 请注意“最简单的概述版本”。 - Fattie
这在过去十年中发生了巨大的变化,请滚动到“Simplest 2022 solution”。 - Fattie
18个回答

251

可以使用以下方法实现:

- (void)setViewControllers:(NSArray *)viewControllers 
                 direction:(UIPageViewControllerNavigationDirection)direction 
                  animated:(BOOL)animated 
                completion:(void (^)(BOOL finished))completion;`

这是在页面上设置第一个视图控制器的相同方法。类似地,您可以使用它来转到其他页面。

想知道为什么viewControllers是一个数组,而不是单个视图控制器吗?

因为页面视图控制器可以有一个“脊柱”(就像iBooks中一样),一次显示2页内容。如果一次只显示1页内容,则只需传递一个1元素数组。

以下是Swift中的示例:

pageContainer.setViewControllers([displayThisViewController], direction: .Forward, animated: true, completion: nil)

2
现在NDA已经解除,能否再详细介绍一下?可以提供一些代码示例吗? - SpaceTrucker
12
我认为我终于明白了:UIPageViewController并不真正关心当前在哪一页。当你调用setViewControllers时,它可以让你看起来正在动态地从当前视图控制器中导航离开,但它实际上并不是在寻找页面。 - Amy
3
[self setViewControllers:@[ vcToMoveTo ] direction:UIPageViewControllerNavigationDirectionForward animated:YES completion:nil]; // 其中vcToMoveTo是一个UIViewController,该代码用于设置UIPageViewController的当前视图控制器,并以向前滑动的方式进行动画切换。 - finneycanhelp
1
如果您想继续使用UIPageControl,则可以考虑以下操作: self.currentIndex = viewControllerToMoveTo的索引,然后在presentationIndexForPageViewController中返回self.currentIndex。 - brendan
4
请查看 https://dev59.com/aGEh5IYBdhLWcg3wVCAb,如果您还需要更新页面指示器的显示。 - Protongun
显示剩余2条评论

37

因为我也需要这个,所以我将详细介绍如何执行此操作。

注意:我假设您使用了生成UIPageViewController结构的标准模板表单,它在调用时同时创建了modelViewControllerdataViewController。如果您不理解我写的内容,请返回并创建一个使用UIPageViewController作为基础的新项目。然后您就会明白。

因此,需要翻转到特定页面涉及设置上述方法的各个部分。在本例中,我假设它是一个由两个视图显示的横向视图。此外,我将其实现为IBAction,以便可以通过按钮按下或其他方式完成 - 同样易于使其成为选择器调用并传递所需的项。

因此,在此示例中,您需要显示的两个视图控制器 - 可选地,您要前进还是后退。

请注意,我仅硬编码了前往第4页和第5页的位置,并使用向前滑动。

从这里您可以看到,您只需要传递将帮助您获取这些项的变量即可......

-(IBAction) flipToPage:(id)sender {

    // Grab the viewControllers at position 4 & 5 - note, your model is responsible for providing these.  
    // Technically, you could have them pre-made and passed in as an array containing the two items...

    DataViewController *firstViewController = [self.modelController viewControllerAtIndex:4 storyboard:self.storyboard];
    DataViewController *secondViewController = [self.modelController viewControllerAtIndex:5 storyboard:self.storyboard];

    //  Set up the array that holds these guys...

    NSArray *viewControllers = nil;

    viewControllers = [NSArray arrayWithObjects:firstViewController, secondViewController, nil];

    //  Now, tell the pageViewContoller to accept these guys and do the forward turn of the page.
    //  Again, forward is subjective - you could go backward.  Animation is optional but it's 
    //  a nice effect for your audience.

      [self.pageViewController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:YES completion:NULL];

    //  Voila' - c'est fin!  

}

2
我有同样的需求,需要跳转到特定页面。但是上面的代码似乎对我不起作用。我做错了什么?我有19页。 - coder
我们在使用数据源对象提供视图控制器时,是否也可以使用这段代码片段? - Jens Kohl
21
这个类的实现非常奇怪而且很不好,苹果。 - devios1

31

这是另一个单页视图的代码示例。此处省略了viewControllerAtIndex的实现,它应该返回给定索引的正确视图控制器。

- (void)changePage:(UIPageViewControllerNavigationDirection)direction {
    NSUInteger pageIndex = ((FooViewController *) [_pageViewController.viewControllers objectAtIndex:0]).pageIndex;

    if (direction == UIPageViewControllerNavigationDirectionForward) {
        pageIndex++;
    }
    else {
        pageIndex--;
    }

    FooViewController *viewController = [self viewControllerAtIndex:pageIndex];

    if (viewController == nil) {
        return;
    }

    [_pageViewController setViewControllers:@[viewController]
                                  direction:direction
                                   animated:YES
                                 completion:nil];
}

通过调用相关函数,可以更改页面。

[self changePage:UIPageViewControllerNavigationDirectionReverse];
[self changePage:UIPageViewControllerNavigationDirectionForward];

但是页面索引控件没有得到更新。我该怎么做? - thatzprem
1
这个答案对我有用 - 请注意,如果您依赖于UIPageViewController的“didFinishAnimating”委托方法,它们将不会被触发。相反,将您的代码从didFinishAnimating移动到一个单独的方法中,并从setViewControllers方法的完成块中调用该方法。 - DiscDev
1
如果您想继续使用UIPageControl,则可以尝试以下操作: self.currentIndex = viewControllerToMoveTo的索引,然后在presentationIndexForPageViewController中返回self.currentIndex。 - brendan
1
@thatzprem 是正确的。每次都会跳转到同一页。例如,如果我们有3页,则每次都只会跳转到第2页。 - Tushar Lathiya

25

也许我会漏掉了什么,但是这个解决方案看起来比其他提出的方案要简单得多。

extension UIPageViewController {
    func goToNextPage(animated: Bool = true, completion: ((Bool) -> Void)? = nil) {
        if let currentViewController = viewControllers?[0] {
            if let nextPage = dataSource?.pageViewController(self, viewControllerAfter: currentViewController) {
                setViewControllers([nextPage], direction: .forward, animated: animated, completion: completion)
            }
        }
    }
}

4
运作良好!只需以某种方式更改页面指示器即可。 - TruMan1
非常有帮助!请给我们提供一个goToFirstPage函数。 - Mamta
每次都会跳转到同一页。例如,如果我们有3页,则每次都将只跳转到第2页。 - Tushar Lathiya
很棒的解决方案。 - Behzad Moulodi

15

这段代码对我很有帮助,谢谢。

这是我用它做的事情。一些向前或向后步进的方法,以及一个直接跳转到特定页面的方法。它适用于纵向视图的6页文档。如果您将其粘贴到pageViewController模板的RootController实现中,它将正常工作。

-(IBAction)pageGoto:(id)sender {

    //get page to go to
    NSUInteger pageToGoTo = 4;

    //get current index of current page
    DataViewController *theCurrentViewController = [self.pageViewController.viewControllers   objectAtIndex:0];
    NSUInteger retreivedIndex = [self.modelController indexOfViewController:theCurrentViewController];

    //get the page(s) to go to
    DataViewController *targetPageViewController = [self.modelController viewControllerAtIndex:(pageToGoTo - 1) storyboard:self.storyboard];

    DataViewController *secondPageViewController = [self.modelController viewControllerAtIndex:(pageToGoTo) storyboard:self.storyboard];

    //put it(or them if in landscape view) in an array
    NSArray *theViewControllers = nil;    
    theViewControllers = [NSArray arrayWithObjects:targetPageViewController, secondPageViewController, nil];


    //check which direction to animate page turn then turn page accordingly
    if (retreivedIndex < (pageToGoTo - 1) && retreivedIndex != (pageToGoTo - 1)){
        [self.pageViewController setViewControllers:theViewControllers direction:UIPageViewControllerNavigationDirectionForward animated:YES completion:NULL];
    }

    if (retreivedIndex > (pageToGoTo - 1) && retreivedIndex != (pageToGoTo - 1)){
        [self.pageViewController setViewControllers:theViewControllers direction:UIPageViewControllerNavigationDirectionReverse animated:YES completion:NULL];
    }

}


-(IBAction)pageFoward:(id)sender {

    //get current index of current page
    DataViewController *theCurrentViewController = [self.pageViewController.viewControllers objectAtIndex:0];
    NSUInteger retreivedIndex = [self.modelController indexOfViewController:theCurrentViewController];

    //check that current page isn't first page
    if (retreivedIndex < 5){

        //get the page to go to
        DataViewController *targetPageViewController = [self.modelController viewControllerAtIndex:(retreivedIndex + 1) storyboard:self.storyboard];

        //put it(or them if in landscape view) in an array
        NSArray *theViewControllers = nil;    
        theViewControllers = [NSArray arrayWithObjects:targetPageViewController, nil];

        //add page view
        [self.pageViewController setViewControllers:theViewControllers direction:UIPageViewControllerNavigationDirectionForward animated:YES completion:NULL];

    }

}


-(IBAction)pageBack:(id)sender {


    //get current index of current page
    DataViewController *theCurrentViewController = [self.pageViewController.viewControllers objectAtIndex:0];
    NSUInteger retreivedIndex = [self.modelController indexOfViewController:theCurrentViewController];

    //check that current page isn't first page
    if (retreivedIndex > 0){

    //get the page to go to
    DataViewController *targetPageViewController = [self.modelController viewControllerAtIndex:(retreivedIndex - 1) storyboard:self.storyboard];

    //put it(or them if in landscape view) in an array
    NSArray *theViewControllers = nil;    
    theViewControllers = [NSArray arrayWithObjects:targetPageViewController, nil];

    //add page view
    [self.pageViewController setViewControllers:theViewControllers direction:UIPageViewControllerNavigationDirectionReverse animated:YES completion:NULL];

    }

}

我想在横向模式下使用它,其中存在两个页面。如何在那里使用它?你能指导一下吗? - iPhone Programmatically

10

Swift 4:

当我在页面控制器视图控制器上点击下一步并更新页面控制器索引时,我需要进入下一个控制器,所以以下是对我来说最好和最直观的解决方案:

let pageController = self.parent as! PageViewController

pageController.setViewControllers([parentVC.orderedViewControllers[1]], direction: .forward, animated: true, completion: nil)

pageController.pageControl.currentPage = 1

5
使用dataSource中的方法怎么样?
UIViewController *controller = [pageViewController.dataSource pageViewController:self.pageViewController viewControllerAfterViewController:pageViewController.viewControllers.firstObject];
[pageViewController setViewControllers:@[controller] direction:UIPageViewControllerNavigationDirectionForward animated:YES completion:nil];

类比地考虑后向的情况。

3

Swift中:

import UIKit

extension HomeViewController {

    // MARK: - Carousel management.

    func startTimerForMovingCarousel(let numberOfSecondsBetweenFiringsOfTheTimer: NSTimeInterval) {
        if (self.timerForMovingCarousel == nil) {
            self.timerForMovingCarousel = NSTimer.scheduledTimerWithTimeInterval(numberOfSecondsBetweenFiringsOfTheTimer,
                                                                            target: self,
                                                                            selector: "moveCarousel",
                                                                            userInfo: nil,
                                                                            repeats: true)
        }
    }

    func moveCarousel() {
        println("I move the carousel.")

        let currentContentViewControllerDisplayed = self.pageViewController!.viewControllers[0] as! ContentViewController

        var index: Int = currentContentViewControllerDisplayed.index

        if index == self.arrayViewControllers.count - 1 {
            index = 0
        } else {
            ++index
        }

        let contentViewControllerToDisplay = self.arrayViewControllers[index] as! ContentViewController

        self.pageViewController.setViewControllers([contentViewControllerToDisplay],
                                                    direction: UIPageViewControllerNavigationDirection.Forward,
                                                    animated: true,
                                                    completion: nil)

        self.pageControl.currentPage = contentViewControllerToDisplay.index
    }

    func invalidateTimerForMovingCarousel() {
        if (self.timerForMovingCarousel != nil) {
            self.timerForMovingCarousel.invalidate()
            self.timerForMovingCarousel = nil
        }
    }

}

1
请问您能否给我一个完整的例子呢?因为我正在做类似的事情。如何在我的代码中使用这个扩展。 如果您愿意,我可以把代码发给您。 - Saty

3

然而,使用“self.pageViewController setViewControllers”方法调用不会在已经翻页的视图控制器上调用“viewDidDissapear”,而手动翻页会在已经翻过去的页面上调用“viewDidDissapear”。我相信这是导致我的崩溃的原因。

“提供的视图控制器数量(0)与所请求的转换所需的数量(1)不匹配”。


请尝试使用以下方法:pageViewController:didFinishAnimating:previousViewControllers:transitionCompleted: - Graham

2
- (void)moveToPage {

    NSUInteger pageIndex = ((ContentViewController *) [self.pageViewController.viewControllers objectAtIndex:0]).pageIndex;

    pageIndex++;

    ContentViewController *viewController = [self viewControllerAtIndex:pageIndex];

if (viewController == nil) {
        return;
    }
    [self.pageViewController setViewControllers:@[viewController] direction:UIPageViewControllerNavigationDirectionForward animated:YES completion:nil];

}

//现在调用这个方法 [self moveToPage]; 希望它能正常工作 :)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接