UIPageViewController,如何正确跳转到特定页面,而不会打乱数据源指定的顺序?

64

我发现了一些关于如何使 UIPageViewController 跳转到特定页面的问题,但是我注意到在跳转时会出现一个额外的问题,而且没有任何答案似乎意识到这点。

不想详细介绍我的iOS应用程序(类似于分页日历),以下是我的经验。我声明了一个 UIPageViewController,设置了当前视图控制器,并实现了数据源。

// end of the init method
        pageViewController = [[UIPageViewController alloc] 
        initWithTransitionStyle:UIPageViewControllerTransitionStyleScroll
          navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal
                        options:nil];
        pageViewController.dataSource = self;
        [self jumpToDay:0];
}

//...

- (void)jumpToDay:(NSInteger)day {
        UIViewController *controller = [self dequeuePreviousDayViewControllerWithDaysBack:day];
        [pageViewController setViewControllers:@[controller]
                                    direction:UIPageViewControllerNavigationDirectionForward
                                     animated:YES
                                   completion:nil];
}

- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController {
        NSInteger days = ((THDayViewController *)viewController).daysAgo;
        return [self dequeuePreviousDayViewControllerWithDaysBack:days + 1];
}

- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController {
        NSInteger days = ((THDayViewController *)viewController).daysAgo;
        return [self dequeuePreviousDayViewControllerWithDaysBack:days - 1];
}

- (UIViewController *)dequeuePreviousDayViewControllerWithDaysBack:(NSInteger)days {
        return [[THPreviousDayViewController alloc] initWithDaysAgo:days];
}

编辑注:我为出队方法添加了简化的代码。即使使用这种亵渎的实现,我仍然面临着与页面顺序完全相同的问题。

初始化工作都按预期进行。增量分页也正常工作。问题在于,如果我再次调用 jumpToDay,顺序就会混乱。

如果用户在第-5天,跳到第1天,向左滚动将再次显示第-5天,而不是适当的第0天。这似乎与 UIPageViewController 如何保留对附近页面的引用有关,但我找不到任何强制刷新缓存的方法。

有什么建议吗?


我认为问题与页面控制器保留对附近页面的引用方式无关 - 我甚至不认为它会这样做。在您的数据源实现中,跟踪页面是您的责任。因此,daysAgo或dequeuePreviousDayViewControllerWithDaysBack:方法可能存在问题。 - rdelmar
你解决了这个问题吗?我也遇到了同样的问题。 - Seb Kade
1
@rdelmar 通常我会承认这是我的问题,但“dequeue”方法实际上只是通过一个内部引用给定的“向后天数”来实例化相同类型的控制器。它甚至不执行任何复杂的排队和分配操作,这些任务都由UITableView的dequeueing方法处理。 - Kyle
@SebKade 我还没有找到解决方案,但由于这需要在今天完成,我考虑自己编写页面视图控制器类。如果成功了,我会告诉你的。 - Kyle
那么,daysAgo可能有问题,因为它是唯一决定哪个控制器被实例化的东西。至于自己编写,如果您一次只做一个页面,那么在我看来,页面视图控制器对您并没有太大帮助。我做了一个称为环形控制器的控制器,它允许您向前和向后翻页,还可以跳转到任何页面。实现起来并不难,但我的当前实现会提前实例化所有页面控制器,这并不是很内存高效。 - rdelmar
显示剩余3条评论
13个回答

1
如果你不需要动画到新页面,就像我一样,下面的代码对我有用,在故事板中调用“值更改”时。而不是在视图控制器之间进行更改,我更改与当前视图控制器相关联的数据。
    - (IBAction)pageControlCurrentPageDidChange:(id)sender
{
    self.currentIndex = self.pageControl.currentPage;
    MYViewController *currentPageViewController = (MYViewController *)self.pageViewController.viewControllers.firstObject;
    currentPageViewController.pageData = [self.pageDataSource dataForPage:self.currentIndex];
    [currentPageViewController updateDisplay];
}

currentIndex的作用是在页面之间滑动时更新pageControl的当前页。

pageDataSource dataForPage: 返回一个数据对象数组,这些对象由页面显示。


你能删减 MYViewController 类的实现吗?因为我没有看到任何 iOS ViewController 类有 pageData 属性和 updateDisplay 方法。 - Josip B.
pageData是MYViewController中的一个属性,可以是任何东西来保存视图控制器的数据,例如字典或核心数据管理对象。updateDisplay只是一种显示页面数据内容的方法。这些都是MYViewController自定义的内容,当然也是一个示例视图控制器名称。 - Erich Wood

1
    let orderedViewControllers = [UIViewController(),UIViewController(), UIViewController()]
    let pageViewController = UIPageViewController()
    let pageControl = UIPageControl()

    func jump(to: Int, completion: @escaping (_ vc: UIViewController?) -> Void){

        guard orderedViewControllers.count > to else{
            //index of bounds
            return
        }

        let toVC = orderedViewControllers[to]

        var direction: UIPageViewController.NavigationDirection = .forward

        if pageControl.currentPage < to {
            direction = .forward;
        } else {
            direction = .reverse;
        }

        pageViewController.setViewControllers([toVC], direction: direction, animated: true) { _ in
            DispatchQueue.main.async {
                self.pageViewController.setViewControllers([toVC], direction: direction, animated: false){ _ in
                    self.pageControl.currentPage = to
                        completion(toVC)

                }
            }
        }
    }

使用方法:

self.jump(to: 5) { (vc) in
    // you can do anything for new vc.
}

0

我自己也曾经为这个问题苦苦挣扎过很长一段时间。对我来说,我有一个从Storyboard中加载的UIPageViewController(我称之为PageController),在上面我添加了一个UIViewController 'ContentVC'。

我让ContentVC负责将数据加载到内容区域,并让PageController负责滑动/跳转/页面指示器的更新。ContentVC有一个实例变量CurrentPageIndex,并将该值发送给PageController,以便PageController知道自己所在的页面。在我的包含PageController的.m文件中,我有以下两个方法。

请注意,我用set将其设置为0,因此每次PageVC重新加载时它都会跳转到第一页,而我并不希望如此,[self viewControllerAtIndex:0]。

- (void)setPageForward
{  
  ContentVC *FirstVC = [self viewControllerAtIndex:[CurrentPageIndex integerValue]];

  NSArray *viewControllers = @[FirstVC];
  [PageController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:nil];
}

这种方法是PageViewController的DataSource方法。presentationIndexForPageViewController将把高亮点设置为正确的页面(你想要的页面)。请注意,如果我们在这里返回0,页面指示器将突出显示表示第一页的第一个点,而我们并不希望如此。

- (NSInteger)presentationIndexForPageViewController:(UIPageViewController *)pageViewController 
{
  return [CurrentPageIndex integerValue];
}

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