iOS — метод UIPageViewControllerDataSource вызывается дважды

Я обнаружил странную проблему с двумя методами UIPageViewControllerDataSource: когда я впервые вхожу в сцену контроллера представления страницы и перетаскиваю содержимое контроллера, независимо от направления перетаскивания, вызываются оба метода источника данных. Я полагаю, что когда я нахожусь на первой странице и перетаскиваю вправо, что означает, что перед первой страницей больше нет страницы, ни один из методов не должен вызываться. И если я перетащу влево, должен быть вызван только метод after.

Я следил за этой публикацией, чтобы настроить контроллеры представления (за исключением того, что у меня нет отдельного контроллера представления страницы на раскадровке. Я использовал традиционный метод [UIPAgeViewController alloc] init] для создания экземпляра контроллера). Ниже мой код:

Для контроллера представления, который фактически отображает содержимое контроллера представления страницы (показана только соответствующая часть viewDidLoad):

 - (void)viewDidLoad 
{
    // initialize page view controller
    _pageViewController = [[UIPageViewController alloc] initWithTransitionStyle:UIPageViewControllerTransitionStyleScroll navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal options:nil];
    self.pageViewController.delegate = self;
    self.pageViewController.dataSource = self;

    // set up the initial scene of the page view controller
    UIViewController *viewController = [self viewControllerAtIndex:0];
    [self.pageViewController setViewControllers:@[viewController]
                                      direction:UIPageViewControllerNavigationDirectionForward
                                       animated:NO
                                     completion:nil];

    // adjust the size of the page view controller, -44 to show the buttons at the bottom
    self.pageViewController.view.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height - 44);

    // display the content of the page view controller
    [self addChildViewController:self.pageViewController];
    [self.view addSubview:self.pageViewController.view];
    [self.pageViewController didMoveToParentViewController:self];
}

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

- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController
       viewControllerAfterViewController:(UIViewController *)viewController
{
    NSUInteger index = ((PWCPageContentViewController *)viewController).index;
    if (index == self.numberOfPages - 1 || index == NSNotFound) {
        return nil;
    }
    ++index;
    return [self viewControllerAtIndex:index];
}

- (PWCPageContentViewController *)viewControllerAtIndex:(NSUInteger)index
{
    PWCPageContentViewController *viewController =
    [self.storyboard instantiateViewControllerWithIdentifier:@"PageContentViewController"];
    viewController.index = index;
    NSString *text = [self.notes getNoteAtIndex:index];
    [viewController.textView setText:text];

    return viewController;
}

Для PWCPageContentViewController.h:

#import <UIKit/UIKit.h>

@class PWCNotes;

@interface PWCPageContentViewController : UIViewController <UITextViewDelegate>

@property NSUInteger index;
@property (weak, nonatomic) IBOutlet UITextView *textView;

- (void)dismissKeyboard;

@end

Для PWCPageContentViewController.m:

#import "PWCPageContentViewController.h"

@interface PWCPageContentViewController ()

@end

@implementation PWCPageContentViewController

- (void)viewDidLoad
{
    [super viewDidLoad];

    self.textView.delegate = self;
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

- (void)dismissKeyboard
{
    if ([self.textView isFirstResponder]) {
        // dismiss the keyboard when hitting save
        [self.textView resignFirstResponder];
    }
}

@end

Так это известная проблема или я что-то упускаю?


person ljiatu    schedule 08.04.2014    source источник
comment
Можете ли вы поделиться кодом?   -  person Rashad    schedule 08.04.2014


Ответы (4)


UIPageViewController использует приведенный ниже метод для получения заднего и переднего ViewController.

- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController;
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController;

Хотя это странно, но да, это известная проблема. Каждый раз, когда он вызывает метод Before и After для получения VC. Если нет следующего VC, он возвращает nil, а если нет предыдущего VC, datasourceDelegate возвращает nil, иначе возвращает индекс VC.

В UIPageViewControllerDelegate есть функция с именем:

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

Посмотрите на это, это может помочь получить текущий или следующий/предыдущий контроллер представления из массива pendingViewControllers.

Надеюсь это поможет.. :)

person Rashad    schedule 08.04.2014
comment
Итак, если контроллер представления страницы отображает только один контроллер представления, будет ли в массиве pendingViewControllers только один контроллер представления? - person ljiatu; 08.04.2014
comment
Должно быть. Я никогда не сталкивался с таким случаем, но ответ должен быть ДА. - person Rashad; 08.04.2014
comment
Благодарю вас! Этот обходной путь идеально подходит для моего случая. Я хотел бы отправить отчет об ошибке в Apple, так как я не видел, чтобы проблема упоминалась в их списке известных проблем. - person ljiatu; 08.04.2014
comment
Однако есть одна проблема: метод willTransitionToViewControllers не вызывается при загрузке начальной сцены, поэтому тексты не отображаются для первого текстового представления. Я пытался настроить текстовое представление для начальной сцены в viewDidLoad, но это не работает. Любая идея, как справиться с этим угловым случаем? - person ljiatu; 08.04.2014
comment
NVM, я понял, как заставить его работать. Первоначально я установил текстовое представление контроллера содержимого, прежде чем настраивать контроллер содержимого, который будет содержаться в контроллере представления страницы. Я просто изменил порядок, и он работает по волшебству. - person ljiatu; 08.04.2014
comment
Как вы заставили это работать, также поделитесь этим, это может помочь другим. - person Rashad; 08.04.2014

Решение этой проблемы в Swift:

Я столкнулся с ошибкой, когда использовал более 3 UIPageViewController (попробуйте, например, 4 UIPageViewController, используя фрагмент кода @Rashad). Проблема возникла из-за отсутствующего и нереализованного дополнительного метода UIPageViewControllerDelegate, который сводил UIPageControl с ума и делал его нестабильным.

Пожалуйста, найдите конкретный метод, который исправляет ошибку ниже:

/*
    Optional UIPageViewControllerDelegate method

    Sent when a gesture-initiated transition ends. The 'finished' parameter indicates whether
    the animation finished, while the 'completed' parameter indicates whether the transition completed or bailed out (if the user let go early).
*/
func pageViewController(pageViewController: UIPageViewController,
    didFinishAnimating finished: Bool,
    previousViewControllers: [AnyObject],
    transitionCompleted completed: Bool)
{
    // Turn is either finished or aborted
    if (completed && finished) {
        let currentDisplayedViewController = self.pageViewController!.viewControllers[0] as! ContentViewController
        self.pageControl.currentPage = currentDisplayedViewController.index
    }
}

Если вы хотите получить полностью работающий проект, перейдите по ссылке на Github ниже:

(ссылка проекта на Github)

person King-Wizard    schedule 25.05.2015
comment
Я пробовал это, но не похоже, что эта функция вызывается... есть идеи, почему бы и нет? - person OOProg; 13.02.2016
comment
Проверьте проект github, упомянутый в ответе, загрузите его и попробуйте запустить и прочитать исходный код. Затем продублируйте мой исходный код и адаптируйте его под свои нужды. Ваше здоровье. - person King-Wizard; 13.02.2016
comment
Большое спасибо, я несколько дней возился с этой проблемой; теперь работает как шарм! - person stumped; 17.05.2016

Итак, как предложил @Rashad, pageViewController:willTransitionToViewControllers: не запутался. Он вызывается ровно один раз при переходе к предыдущему/следующему контроллеру представления (в случае, если предыдущий/следующий контроллер представления не nil). Это также относится к pageViewController:didFinishAnimating:previousViewControllers:transitionCompleted:, который вызывается ровно один раз, когда анимация заканчивается.

Так что, если вы хотите делать какие-либо вещи на основе индекса и хотите получить правильный индекс для будущих контроллеров представления, или вы хотите получить правильный контроллер представления, рассмотрите возможность использования этих двух методов в качестве обходного пути на данный момент.

person ljiatu    schedule 08.04.2014
comment
Как именно я могу получить индекс из этого метода? пожалуйста помоги - person N. Der; 28.01.2019

изменить стиль перехода с прокрутки на pageCurl

pageViewController = UIPageViewController (transitionStyle: .pageCurl, navigationOrientation: .horizontal, параметры: nil)

это выглядит лучше, и это не будет вызываться дважды

person Sandeep Yadav    schedule 30.05.2017