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

169

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


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

2
如果您希望通过滚动和单击自定义按钮在页面视图控制器中导航页面,并且需要使用PAGE CONTROL来指示当前页面为O,则以下内容可能会有所帮助。请注意保留HTML标签。
//Set Delegate & Data Source for PageView controller [Say in View Did Load]
   self.pageViewController.dataSource = self;
   self.pageViewController.delegate = self;

// Delegates called viewControllerBeforeViewController when User Scroll to previous page 

 - (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController{

    [_nextBtn setTitle:@"Next" forState:UIControlStateNormal];

    NSUInteger index = ((PageContentViewController*) viewController).pageIndex;

    if (index == NSNotFound){
        return nil;
    }else if (index > 0){
        index--;
    }else{
        return nil;
    }        
    return [self viewControllerAtIndex:index];        
}

// 当用户滚动到下一页时,委托调用viewControllerAfterViewController

- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController{

    NSUInteger index = ((PageContentViewController*) viewController).pageIndex;

if (index == NSNotFound){
    return nil;
}else if (index < 3){
    index++;
}else{
    return nil;
}
return [self viewControllerAtIndex:index];}

//Delegate called while transition of pages. The need to apply logic in this delegate is to maintain the index of current page.

 - (void)pageViewController:(UIPageViewController *)pageViewController willTransitionToViewControllers:(NSArray *)pendingViewControllers{ 

buttonIndex = (int)((PageContentViewController*) pendingViewControllers.firstObject).pageIndex;

}

-(void)pageViewController:(UIPageViewController *)pageViewController didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray *)previousViewControllers transitionCompleted:(BOOL)completed{    
if (buttonIndex == 0){
    _backButton.hidden = true;
}else if (buttonIndex == [self.pageImages count] - 1){
    _backButton.hidden = false;
    [_nextBtn setTitle:@"Begin" forState:UIControlStateNormal];
}else{
    _backButton.hidden = false;
}

//自定义按钮(上一步和下一步)的逻辑

-(void)backBtnClicked:(id)sender{
    if (buttonIndex > 0){
buttonIndex -= 1;
    }
    if (buttonIndex < 1) {
        _backButton.hidden = YES;
    }
    if (buttonIndex >=0) {
         [_nextBtn setTitle:@"Next" forState:UIControlStateNormal];
        PageContentViewController *startingViewController = [self viewControllerAtIndex:buttonIndex];
        NSArray *viewControllers = @[startingViewController];
        [self.pageViewController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:nil];
    }

}

-(void)nextBtnClicked:(id)sender{
if (buttonIndex < 3){
buttonIndex += 1;
}
if(buttonIndex == _pageImages.count){
   //Navigate Outside Pageview controller        
}else{        
    if (buttonIndex ==3) {

        [_nextBtn setTitle:@"Begin" forState:UIControlStateNormal];
    }
    _backButton.hidden = NO;

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

}

//BUTTON INDEX & MAX COUNT
-(NSInteger)presentationCountForPageViewController:(UIPageViewController *)pageViewController{
    return [self.pageImages count];}

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

你已经在四个问题中发布了几乎完全相同的答案。如果你认为其中一个是另一个的副本,你应该将它们标记为这样(而不是多次发布相同的答案)。 - Jaap
我可以在其他类似的问题下发布此问题的链接吗,但解决方案是相同的。 - Yogesh Lolusare
1
我不是要评判什么,但你的代码格式让我感到非常不舒服。 - Lukas Petr
1
没错,@Lukas,我现在会立即进行格式化。谢谢。 - Yogesh Lolusare

2

我还需要一个用于在PageViewController中向左导航的按钮,这个按钮应该执行与滑动手势相同的操作。

由于我已经在“pageViewController:viewControllerBeforeViewController”中设置了一些规则以处理自然滑动导航,因此我希望该按钮重用所有该代码,并且不能像之前的答案那样使用特定的索引来达到页面。所以,我必须采取另一种解决方案。

请注意,以下代码适用于我的自定义ViewController内部的一个Page View Controller,其脊柱设置为中间。

这是我为导航左侧按钮编写的代码:

- (IBAction)btnNavigateLeft_Click:(id)sender {

    // Calling the PageViewController to obtain the current left page
    UIViewController *currentLeftPage = [_pageController.viewControllers objectAtIndex:0];

    // Creating future pages to be displayed
    NSArray *newDisplayedPages = nil;
    UIViewController *newRightPage = nil;
    UIViewController *newLeftPage = nil;

    // Calling the delegate to obtain previous pages
    // My "pageViewController:viewControllerBeforeViewController" method returns nil if there is no such page (first page of the book).
    newRightPage = [self pageViewController:_pageController viewControllerBeforeViewController:currentLeftPage];
    if (newRightPage) {
        newLeftPage = [self pageViewController:_pageController viewControllerBeforeViewController:newRightPage];
    }

    if (newLeftPage) {
        newDisplayedPages = [[NSArray alloc] initWithObjects:newLeftPage, newRightPage, nil];
    }

    // If there are two new pages to display, show them with animation.
    if (newDisplayedPages) {
        [_pageController setViewControllers:newDisplayedPages direction:UIPageViewControllerNavigationDirectionReverse animated:YES completion:nil];
    }
}

您可以通过类似的方式创建一个右侧导航按钮。

1

对于单页面,我只是编辑了@Jack Humphries的答案。

-(void)viewDidLoad
{
  counter = 0;
}
-(IBAction)buttonClick:(id)sender
{
  counter++;
  DataViewController *secondVC = [self.modelController viewControllerAtIndex:counter storyboard:self.storyboard];
  NSArray *viewControllers = nil;          
  viewControllers = [NSArray arrayWithObjects:secondVC, nil];          
  [self.pageViewController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:YES completion:NULL];
}

0

最简单的2022解决方案:

无论如何,您都需要这个简单的函数:

var currentIndex: Int {
    if let visibleViewController = viewControllers?.first,
       let ci = pages.firstIndex(of: visibleViewController) {
        return ci
    }
    else {
        return 0
    }
}

然后对任何给定页面进行动画处理就很容易了:

public func go(toIndex: Int) {
    setViewControllers(
        [pages[toIndex]],
        direction: ((currentIndex < toIndex) ? .forward : .reverse),
        animated: true)
}

就是这样。


0
这是一个使用UIPageViewControllerDataSource方法的解决方案:
代码跟踪在UIPageViewController中当前显示的视图控制器。
...
@property (nonatomic, strong) UIViewController *currentViewController;
@property (nonatomic, strong) UIViewController *nextViewController;
...

首先将 currentViewController 初始化为 UIPageViewController 设置的初始视图控制器。

- (void) viewDidLoad {
    [super viewDidLoad];
    ...
    self.currentViewController = self.viewControllers[0];
    ...
    [self.pageViewController setViewControllers: @[self.currentViewController] direction: dir animated: YES completion: nil];
}



然后,在UIPageViewControllerDelegate方法中跟踪currentViewController:pageViewController: willTransitionToViewControllers:pageViewController:didFinishAnimating:previousViewControllers:transitionCompleted:

- (nullable UIViewController *) pageViewController: (UIPageViewController *) pageViewController viewControllerBeforeViewController: (UIViewController *) viewController {
    NSInteger idx = [self.viewControllers indexOfObject: viewController];
    if (idx > 0) {
        return self.viewControllers[idx - 1];
    } else {
        return nil;
    }
}

- (nullable UIViewController *) pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController: (UIViewController *) viewController {
    NSInteger idx = [self.viewControllers indexOfObject: viewController];
    if (idx == NSNotFound) {
        return nil;
    } else {
        if (idx + 1 < self.viewControllers.count) {
            return self.viewControllers[idx + 1];
        } else {
            return nil;
        }
    }
}

- (void) pageViewController:(UIPageViewController *)pageViewController willTransitionToViewControllers:(NSArray<UIViewController *> *)pendingViewControllers {
    self.nextViewController = [pendingViewControllers firstObject];
}

- (void)pageViewController:(UIPageViewController *)pageViewController didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray<UIViewController *> *)previousViewControllers transitionCompleted:(BOOL)completed {
    if (completed) {
        self.currentViewController = self.nextViewController;
    }
}

最后,在您选择的方法中(在下面的示例中,它是 -(IBAction)onNext:(id)sender ),您可以使用 UIPageViewControllerDataSouce 方法 pageViewController:viewControllerBeforeViewController:pageViewController:viewControllerAfterViewController:获取下一个或上一个视图控制器,并通过调用 [self.pageViewController setViewControllers:@ [vc] direction:UIPageViewControllerNavigationDirectionForward animated:YES completion:nil];导航到它。

- (void) onNext {
    UIViewController *vc = [self pageViewController: self.tutorialViewController viewControllerAfterViewController: self.currentViewController];
    if (vc != nil) {
        self.currentViewController = vc;
        [self.tutorialViewController setViewControllers: @[vc] direction: UIPageViewControllerNavigationDirectionForward animated: YES completion: nil];
    }
}


一份详细的解释会非常有用。 - Taslim Oseni
@TaslimOseni 感谢您指出示例不够清晰。我已经添加了它的工作原理描述,请告诉我是否需要更多解释。 - pconor

0

我从昨天开始就在处理这个问题,最终我让它工作了。我不能完全使用标准模板,因为我有三个不同的视图控制器作为子视图:

1 - rootViewController;
2 - model;
3 - viewControllers
3.1 - VC1;
3.2 - VC2;
3.3 - VC3.
Note: all of VCs has Storyboard ID.

我只需要在第一个VC中添加一个触发按钮,因此我为pageViewController和model添加了一个属性:

#import <UIKit/UIKit.h>
#import "Model.h"

@interface VC1 : UIViewController

@property (nonatomic, strong) UIPageViewController *thePageViewController;
@property (nonatomic, strong) SeuTimePagesModel *theModel;

@end

我重写了模型的init方法,将storyboard和pageViewController作为参数传递,这样我就可以将它们设置为我的VC1:
- (id)initWithStoryboard:(UIStoryboard *)storyboard
   andPageViewController:(UIPageViewController *)controller
{
    if (self = [super init])
    {
        VC1 *vc1 = [storyboard instantiateViewControllerWithIdentifier:@"VC1"];
        vc1.pageViewController = controller;
        vc1.model = self;

        VC2 *vc2 = [storyboard instantiateViewControllerWithIdentifier:@"VC2"];
        VC3 *vc3 = [storyboard instantiateViewControllerWithIdentifier:@"VC3"];
        self.pageData = [[NSArray alloc] initWithObjects:vc1, vc2, vc3, nil];
    }

    return self;
}

最后,我在我的vc1中添加了一个IBAction来触发pageViewController方法setViewControllers:direction:animated:completion:
- (IBAction)go:(id)sender
{
    VC2 *vc2 = [_model.pages objectAtIndex:1];
    NSArray *viewControllers = @[vc2];
    [_pageViewController setViewControllers:viewControllers
                                  direction:UIPageViewControllerNavigationDirectionForward
                                   animated:YES
                                 completion:nil];
}

注意:我不需要查找VC的索引,我的按钮会带我到第二个VC,但获取所有索引并跟踪子视图并不难:

- (IBAction)selecionaSeuTime:(id)sender
{
    NSUInteger index = [_model.pages indexOfObject:self];
    index++;

    VC2 *vc2 = [_model.pages objectAtIndex:1];
    NSArray *viewControllers = @[vc2];
    [_pageViewController setViewControllers:viewControllers
                                  direction:UIPageViewControllerNavigationDirectionForward
                                   animated:YES
                                 completion:nil];
}

希望这对你有所帮助!


0

正如@samwize所指出的那样,分页的方法是使用

setViewControllers:array

这是我做的方法: 我将数据源和代理分开。您可以调用此方法,仅更改“下一个”视图。以前,您必须应用分页规则。也就是说,您必须检查方向和下一个控制器。如果您使用自定义数据源的该方法,则显示的控制器数组不会更改(因为您将在NSObject子类上使用自定义数组)。
使用自己的数据源,该方法只会设置一个新视图(具有特定方向)。由于通常滑动时仍将控制要显示的页面。

0

由于ViewControllers嵌入在PageViewController中,您只需要执行以下操作:

  • 检索父视图控制器并转换为相关类。
  • 使用该父级数据源属性获取下一个ViewController。
  • 使用SetParentViewControllers方法设置并过渡到下一页。

以下是Xamarin的示例,但与Swift等功能类似。以下代码将在作为页面显示的其中一个ViewControllers中的Button Click委托中:

var parent = ParentViewController as WelcomePageViewController;
var next = parent.DataSource.GetNextViewController(parent, this);

parent.SetViewControllers(new UIViewController[] { next }, UIPageViewControllerNavigationDirection.Forward, true, null);

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