64

特定のページにジャンプする方法についていくつか質問を見つけましたが、UIPageViewControllerジャンプに関する追加の問題に気づきました。どの回答も認めていないようです。

私のiOSアプリ(ページングされたカレンダーに似ています)の詳細に立ち入ることなく、これが私が経験していることです。を宣言しUIPageViewController、現在のView Controllerを設定し、データソースを実装します。

// 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日目にジャンプした場合、左にスクロールすると、適切な0日目ではなく-5日目が再び表示されます。これは、UIPageViewController近くのページへの参照を維持する方法と関係があるようですが、私はできますキャッシュを強制的に更新するメソッドへの参照が見つかりません。

何か案は?

4

13 に答える 13

85

Matt NeuburgによるiOS6のプログラミングは、この正確な問題を文書化しており、私は実際、彼の解決策が現在受け入れられている答えよりも少し良いと感じていることを発見しました。このソリューションはうまく機能しますが、前後の画像をアニメーション化してから、そのページを目的のページにぎこちなく置き換えるというマイナスの副作用があります。それは奇妙なユーザーエクスペリエンスだと感じました。Mattのソリューションがそれを処理します。

__weak UIPageViewController* pvcw = pvc;
[pvc setViewControllers:@[page]
              direction:UIPageViewControllerNavigationDirectionForward
               animated:YES completion:^(BOOL finished) {
                   UIPageViewController* pvcs = pvcw;
                   if (!pvcs) return;
                   dispatch_async(dispatch_get_main_queue(), ^{
                       [pvcs setViewControllers:@[page]
                                  direction:UIPageViewControllerNavigationDirectionForward
                                   animated:NO completion:nil];
                   });
               }];
于 2013-09-03T22:00:03.047 に答える
37

それで、私はあなたと同じ問題に遭遇しました。そこでは、ページに「ジャンプ」できる必要があり、ページをジェスチャで戻すと「順序がめちゃくちゃになっている」ことがわかりました。私が知る限り、ページビューコントローラーは間違いなくビューコントローラーをキャッシュしており、ページに「ジャンプ」するときは、方向(順方向または逆方向)を指定する必要があります。次に、新しいViewControllerが前のViewControllerの「ネイバー」であると想定するため、ジェスチャーで戻ると、前のViewControllerが自動的に表示されます。UIPageViewControllerTransitionStyleScrollこれは、を使用している場合にのみ発生し、を使用していない場合にのみ発生することがわかりましたUIPageViewControllerTransitionStylePageCurl。ページカールスタイルは、「ジャンプ」した場合、明らかに同じキャッシュを実行しません。pageViewController:viewController(Before/After)ViewController:

解決策:ページへの「ジャンプ」を実行する場合、最初にジャンプ先のページ(animated:NO)の隣のページにジャンプし、次にそのジャンプの完了ブロックで目的のページにジャンプできます。これによりキャッシュが更新され、ジェスチャで戻ると正しいネイバーページが表示されます。欠点は、2つのViewControllerを作成する必要があることです。ジャンプ先の1つと、ジェスチャーで戻った後に表示されるはずの1つです。

これが私が書いたカテゴリーのコードですUIPageViewController

@implementation UIPageViewController (Additions)

 - (void)setViewControllers:(NSArray *)viewControllers direction:(UIPageViewControllerNavigationDirection)direction invalidateCache:(BOOL)invalidateCache animated:(BOOL)animated completion:(void (^)(BOOL finished))completion {
    NSArray *vcs = viewControllers;
    __weak UIPageViewController *mySelf = self;

    if (invalidateCache && self.transitionStyle == UIPageViewControllerTransitionStyleScroll) {
        UIViewController *neighborViewController = (direction == UIPageViewControllerNavigationDirectionForward
                                                    ? [self.dataSource pageViewController:self viewControllerBeforeViewController:viewControllers[0]]
                                                    : [self.dataSource pageViewController:self viewControllerAfterViewController:viewControllers[0]]);
        [self setViewControllers:@[neighborViewController] direction:direction animated:NO completion:^(BOOL finished) {
            [mySelf setViewControllers:vcs direction:direction animated:animated completion:completion];
        }];
    }
    else {
        [mySelf setViewControllers:vcs direction:direction animated:animated completion:completion];
    }
}

@end

これをテストするためにできることは、新しい「ページベースのアプリケーション」を作成し、特定の暦月に「ジャンプ」してからジェスチャーで戻る「goto」ボタンを追加することです。必ずトランジションスタイルをスクロールに設定してください。

于 2013-02-13T21:12:38.880 に答える
5

私はこの機能を使用しています(私は常に横向き、2ページモードです)

-(void) flipToPage:(NSString * )index {


int x = [index intValue];
LeafletPageContentViewController *theCurrentViewController = [self.pageViewController.viewControllers   objectAtIndex:0];

NSUInteger retreivedIndex = [self indexOfViewController:theCurrentViewController];

LeafletPageContentViewController *firstViewController = [self viewControllerAtIndex:x];
LeafletPageContentViewController *secondViewController = [self viewControllerAtIndex:x+1 ];


NSArray *viewControllers = nil;

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


if (retreivedIndex < x){

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

} else {

    if (retreivedIndex > x ){

        [self.pageViewController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionReverse animated:YES completion:NULL];
      } 
    }
} 
于 2012-11-30T16:19:25.353 に答える
5

以下のサブクラスに使用される私のSwiftソリューションは次のUIPageViewControllerとおりです。

viewControllersの配列をに格納しviewControllerArray、現在のページインデックスをに格納するとしますupdateCurrentPageIndex

  private func slideToPage(index: Int, completion: (() -> Void)?) {
    let tempIndex = currentPageIndex
    if currentPageIndex < index {
      for var i = tempIndex+1; i <= index; i++ {
        self.setViewControllers([viewControllerArray[i]], direction: UIPageViewControllerNavigationDirection.Forward, animated: true, completion: {[weak self] (complete: Bool) -> Void in
          if (complete) {
            self?.updateCurrentPageIndex(i-1)
            completion?()
          }
          })
      }
    }
    else if currentPageIndex > index {
      for var i = tempIndex - 1; i >= index; i-- {
        self.setViewControllers([viewControllerArray[i]], direction: UIPageViewControllerNavigationDirection.Reverse, animated: true, completion: {[weak self] (complete: Bool) -> Void in
          if complete {
            self?.updateCurrentPageIndex(i+1)
            completion?()
          }
          })
      }
    }
  }
于 2015-05-07T22:00:11.007 に答える
3

djibouti33の答えの迅速なバージョン:

weak var pvcw = pageViewController
pageViewController!.setViewControllers([page], direction: UIPageViewControllerNavigationDirection.Forward, animated: true) { _ in
        if let pvcs = pvcw {
            dispatch_async(dispatch_get_main_queue(), {
                pvcs.setViewControllers([page], direction: UIPageViewControllerNavigationDirection.Forward, animated: false, completion: nil)
            })
        }
    }
于 2015-10-19T08:39:44.943 に答える
3

これはiOS10には当てはまらず、承認された回答ソリューションを使用する必要がなくなったことに注意することが重要です。いつものように続けてください。

于 2016-10-25T09:27:33.517 に答える
2

この問題を確認できますが、使用している場合にのみ発生し、使用しUIPageViewControllerTransitionStyleScrollていない場合にのみ発生しUIPageViewControllerTransitionStylePageCurlます。

回避策:目的のページに到達するまで、ループをUIPageViewController setViewControllers作成し、ページめくりごとに呼び出します。

これにより、UIPageViewControllerの内部データソースインデックスの同期が維持されます。

于 2013-02-21T11:19:00.913 に答える
2

これは唯一の解決策です

-(void)buyAction
{
    isFromBuy = YES;
    APPChildViewController *initialViewController = [self viewControllerAtIndex:4];
    viewControllers = [NSArray arrayWithObject:initialViewController];
    [self.pageController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:nil];
}

-(NSInteger)presentationIndexForPageViewController:(UIPageViewController *)pageViewController 
{
    if (isFromBuy) {
        isFromBuy = NO;
        return 5;
    }
    return 0;
}
于 2015-09-04T13:00:21.060 に答える
1

私は別のアプローチをとっていました。あなたのページが初期化後に更新されるように設計されている場合は可能であるはずです:
マニュアルページが選択されたとき、私はフラグを更新します

- (void)scrollToPage:(NSInteger)page animated:(BOOL)animated
{
    if (page != self.currentPage) {
        [self setViewControllers:@[[self viewControllerForPage:page]]
                       direction:(page > self.currentPage ?
                                  UIPageViewControllerNavigationDirectionForward :
                                  UIPageViewControllerNavigationDirectionReverse)
                        animated:animated
                      completion:nil];
        self.currentPage = page;
        self.forceReloadNextPage = YES; // to override view controller automatic page cache
    }
}

- (ScheduleViewController *)viewControllerForPage:(NSInteger)page
{
    CustomViewController * scheduleViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"CustomViewController"];
    scheduleViewController.view.tag = page; // keep track of pages using view.tag property
    scheduleViewController.data = [self dataForPage:page];

    if (self.currentViewController)
        scheduleViewController.calendarLayoutHourHeight = self.currentViewController.calendarLayoutHourHeight;

    return scheduleViewController;
}

次に、次のページに正しいデータをリロードさせます。

- (void)pageViewController:(UIPageViewController *)pageViewController willTransitionToViewControllers:(NSArray *)pendingViewControllers
{
    CustomViewController * nextViewController = [pendingViewControllers lastObject];

    // When manual scrolling occurs, the next page is loaded from UIPageViewController cache
    //  and must be refreshed
    if (self.forceReloadNextPage) {
        // calculate the direction of the scroll to know if to load next or previous page
        NSUInteger page = self.currentPage + 1;
        if (self.currentPage > nextViewController.view.tag) page = self.currentPage - 1;

        nextViewController.data = [self dataForPage:page];
        self.forceReloadNextPage = NO;
    }
}
于 2013-07-25T02:26:19.817 に答える
1

私がしなかったように、新しいページにアニメーション化する必要がない場合は、ストーリーボードの「ValueChanged」で呼び出される次のコードが機能しました。ビューコントローラ間で変更する代わりに、現在のビューコントローラに関連付けられているデータを変更します。

    - (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のcurrentPageを更新できます。

pageDataSource dataForPage:ページによって表示されるデータオブジェクトの配列を返します。

于 2014-02-09T16:29:54.687 に答える
1

これは、 @djibouti33による回答の最新のSwift3 +バージョンで、構文がクリーンアップされています。

weak var weakPageVc = pageVc

pageVc.setViewControllers([page], direction: .forward, animated: true) { finished in
    guard let pageVc = weakPageVc else {
        return
    }

    DispatchQueue.main.async {
        pageVc.setViewControllers([page], direction: .forward, animated: false)
    }
}
于 2017-11-20T14:00:29.237 に答える
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.
}
于 2019-07-12T14:10:32.877 に答える
0

私は長い間この問題に苦しんでいました。私の場合、ストーリーボードからUIPageViewController(私はPageControllerと呼びます)をロードし、その上にUIViewController'ContentVC'を追加しました。

ContentVCにコンテンツ領域にロードされるデータを処理させ、PageControllerにsliding / goto/PageIndicatorの更新を処理させます。ContentVCにはivarCurrentPageIndexがあり、その値をPageControllerに送信するため、PageControllerはどのページにあるかを認識します。PageControllerを含む私の.mファイルには、これら2つのメソッドがあります。

私は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];
}

この2番目のメソッドは、PageViewControllerのDataSourceメソッドです。presentIndexForPageViewControllerは、強調表示されたドットを右側のページ(目的のページ)に設定します。ここで0を返すと、ページインジケーターが最初のページを示す最初のドットを強調表示するので、それは望ましくないことに注意してください。

- (NSInteger)presentationIndexForPageViewController:(UIPageViewController *)pageViewController 
{
  return [CurrentPageIndex integerValue];
}
于 2014-10-24T18:59:32.157 に答える