Переход UIPageViewController 'Несбалансированные вызовы для перехода к началу/концу отображения для' - программирование
Подтвердить что ты не робот

Переход UIPageViewController 'Несбалансированные вызовы для перехода к началу/концу отображения для'

Когда я перемещаюсь через UIPageViewController быстрее, чем его переходная анимация, я получаю 'Unbalanced calls to begin/end appearance transitions for <MyDataViewController>', и одно из двух представлений в ландшафте не отображается, пока я не попытаюсь перевернуть страницу.

У кого-нибудь есть идея решить эту ошибку?

4b9b3361

Ответ 1

Решаемые следующие действия:
1- Объявить флаг, указывающий, что анимация завершена или нет:

BOOL pageAnimationFinished;

2- Установите для этого флага значение true в viewDidLoad:

pageAnimationFinished = YES;

3- Отключить tapGesture для pageViewController и назначить 'self' делегату panGestureRecognizer:

for (UIGestureRecognizer * gesRecog in self.pageViewController.gestureRecognizers)
{
    if ([gesRecog isKindOfClass:[UITapGestureRecognizer class]])
        gesRecog.enabled = NO;
    else if ([gesRecog isKindOfClass:[UIPanGestureRecognizer class]])
        gesRecog.delegate = self;
}

4- Разрешить/запретить panGestureRecognizer с помощью метода делегирования распознавателя жестов:

-(BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
    if ([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]] && ([gestureRecognizer.view isEqual:self.view] || [gestureRecognizer.view isEqual:self.pageViewController.view]))
    {
        UIPanGestureRecognizer * panGes = (UIPanGestureRecognizer *)gestureRecognizer;
        if(!pageAnimationFinished || (currentPage < minimumPage && [panGes velocityInView:self.view].x < 0) || (currentPage > maximumPage && [panGes velocityInView:self.view].x > 0))
            return NO;
        pageAnimationFinished = NO;
    }
    return YES;
}

5- Добавьте следующий метод делегата viewViewController:

- (void)pageViewController:(UIPageViewController *)pageViewController didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray *)previousViewControllers transitionCompleted:(BOOL)completed
{
    pageAnimationFinished = YES;
}

Ответ 2

Вышеуказанные ответы были правильными, но я считаю более сложными, чем нужно, и кулинарная книга полезна. Итак, вот что, кажется, работает для меня:

В контроллере представления, который устанавливает и вызывает pageViewController, объявить:

@property (assign)              BOOL pageIsAnimating;

и в viewDidLoad:

    pageIsAnimating = NO;

добавить это:

- (void)pageViewController:(UIPageViewController *)pageViewController willTransitionToViewControllers:(NSArray *)pendingViewControllers {
    pageIsAnimating = YES;
}

и добавьте пару строк:

- (void)pageViewController:(UIPageViewController *)pageViewController
    didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray *)previousViewControllers
   transitionCompleted:(BOOL)completed {
    if (completed || finished)   // Turn is either finished or aborted
        pageIsAnimating = NO;
    ...
}

Жесты подавляются путем отказа от предоставления информации о контроллере просмотра:

- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController
   viewControllerAfterViewController:(UIViewController *)viewController {
    if (pageIsAnimating)
        return nil;
    ...
    return after;
}

- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController
  viewControllerBeforeViewController:(UIViewController *)viewController {
    if (pageIsAnimating)
        return nil;
    ...
    return before;
}

Oh и изменения ориентации reset флаг:

- (UIPageViewControllerSpineLocation)pageViewController:(UIPageViewController *)pageViewController
               spineLocationForInterfaceOrientation:(UIInterfaceOrientation)orientation {
    pageIsAnimating = NO;
    ...
}

Ответ 3

Хороший ответ из Basem Saadawy, но у него есть дефект.

На самом деле делегат gestureRecognizerShouldBegin: может быть вызван без дальнейшей анимации. Это возможно, если вы начинаете свой жест вертикальным движением пальца, а его горизонтальное смещение недостаточно для запуска анимации (но этого достаточно для запуска gestureRecognizerShouldBegin:). Таким образом, наша переменная pageAnimationFinished будет установлена ​​на НЕТ без фактической анимации. Поэтому pageViewController: didFinishAnimating: никогда не будет вызываться, и вы можете заблокировать текущую страницу без возможности ее изменения.

Для этого лучше назначить NO эту переменную - метод действия распознавателя жестов с проверкой его скорости и сдвига (нас интересует только горизонтальное направление).

Итак, последние шаги:

1) Объявите переменную экземпляра (флаг):

BOOL pageAnimationFinished;

2) Задайте начальное значение

- (void)viewDidLoad
{
    [super viewDidLoad];
    ...
    pageAnimationFinished = YES;
}

3) Назначьте делегат и настраиваемое действие распознающим жесты.

for (UIGestureRecognizer * gesRecog in self.pageViewController.gestureRecognizers)
{
    if ([gesRecog isKindOfClass:[UIPanGestureRecognizer class]])
    {
        gesRecog.delegate = self;
        [gr addTarget:self action:@selector(handlePan:)];
    }
}

3 ') Анимация действительно начинается, когда перевод жестов больше в горизонтальном направлении, и палец движется горизонтально в одно мгновение.
Я предполагаю, что та же логика используется во внутреннем распознавательном действии, назначаемом UIPageViewController.

- (void) handlePan:(UIPanGestureRecognizer *)gestureRecognizer
{
    if (pageAnimationFinished && gestureRecognizer.state == UIGestureRecognizerStateChanged)
    {
        CGPoint vel = [gestureRecognizer velocityInView:self.view];
        CGPoint tr = [gestureRecognizer translationInView:self.view];
        if (ABS(vel.x) > ABS(vel.y) && ABS(tr.x) > ABS(tr.y))
            pageAnimationFinished = NO; // correct place
    }
}

4) Запрет жестов, если анимация не завершена.

-(BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
    if ([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]] && ([gestureRecognizer.view isEqual:self.view] || [gestureRecognizer.view isEqual:self.pageViewController.view]))
    {
        UIPanGestureRecognizer * panGes = (UIPanGestureRecognizer *)gestureRecognizer;
        if(!pageAnimationFinished || (currentPage < minimumPage && [panGes velocityInView:self.view].x < 0) || (currentPage > maximumPage && [panGes velocityInView:self.view].x > 0))
            return NO;
    }
    return YES;
}

5) Анимация завершена

- (void)pageViewController:(UIPageViewController *)pageViewController didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray *)previousViewControllers transitionCompleted:(BOOL)completed
{
    pageAnimationFinished = YES;
}

Я слишком много играл с ним и, похоже, это хорошее решение, которое хорошо работает.

Ответ 4

Здесь версия QUICK с использованием делегата:

добавьте этот код (убедитесь, что вы включили UIPageViewControllerDelegate в свой заголовок или расширение класса и назначили self.pageViewController.delegate = self;):

- (void)pageViewController:(UIPageViewController *)pageViewController willTransitionToViewControllers:(NSArray *)pendingViewControllers {
   self.pageAnimationFinished = NO;
}

- (void)pageViewController:(UIPageViewController *)pageViewController didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray *)previousViewControllers transitionCompleted:(BOOL)completed {
    self.pageAnimationFinished = YES;
}

затем проверьте self.pageAnimationFinished и верните nil, если он == NO.

Дольше Объяснение:

- (void)pageViewController:(UIPageViewController *)pageViewController didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray *)previousViewControllers transitionCompleted:(BOOL)completed

Мы можем использовать этот метод делегата из UIPageViewControllerDelegate, чтобы узнать, когда анимация перелистает или просматривает страницы. Используя это, мы можем реализовать его следующим образом:

- (void)pageViewController:(UIPageViewController *)pageViewController didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray *)previousViewControllers transitionCompleted:(BOOL)completed {
    pageAnimationFinished = YES;
}

тогда верните nil в свой

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

и

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

когда

pageAnimationFinished == NO. Обязательно установите pageAnimationFinished на NO при анимации. Лучший способ узнать, когда вы живете, - это использовать противоположность

- (void)pageViewController:(UIPageViewController *)pageViewController didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray *)previousViewControllers transitionCompleted:(BOOL)completed

а именно:

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

Я не видел этого предупреждения с тех пор, и это можно сделать в 1/3 строк, как и другие решения. И это намного проще.

Ответ 5

Здесь Swift версия Билла Чесвика отвечает (в настоящее время верхний ответ):

Добавьте переменную для сохранения текущего состояния:

var pageIsAnimating = false

Задайте анимационное состояние:

func pageViewController(pageViewController: UIPageViewController, willTransitionToViewControllers pendingViewControllers: [UIViewController]) {
    self.pageIsAnimating = true
}

func pageViewController(pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
    if finished || completed {
        self.pageIsAnimating = false
    }
}

Заблокируйте переходы, если они в настоящее время оживляют:

func pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController viewController: UIViewController) -> UIViewController? {
    if self.pageIsAnimating {
        return nil
    }

    // Your code here
}

func pageViewController(pageViewController: UIPageViewController, viewControllerAfterViewController viewController: UIViewController) -> UIViewController? {
    if self.pageIsAnimating {
        return nil
    }

    // Your code here
}

Спасибо, Билл Чесвик!

Ответ 6

Мое решение быстро, просто и эффективно:

  • Установите делегат showviewview для вашего класса
  • Добавьте ниже код

    extension MyPageVC: UIPageViewControllerDelegate {
    
        func pageViewController(_ pageViewController: UIPageViewController, willTransitionTo pendingViewControllers: [UIViewController]) {
            self.view.isUserInteractionEnabled = false
        }
    
        func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
            self.view.isUserInteractionEnabled = true
        }
    }
    

Ответ 7

Как насчет этого:

- (void)pageViewController:(UIPageViewController*)pgVC willTransitionToViewControllers:(NSArray*)pendingVCs
{
    pgVC.dataSource = nil; // ... to disallow user to change pages until animation completes
}

- (void)pageViewController:(UIPageViewController*)pgVC
        didFinishAnimating:(BOOL)finished
   previousViewControllers:(NSArray*)prevVCs
       transitionCompleted:(BOOL)completed
{
    if(completed || finished) {
        pgVC.dataSource = _pgViewDataSource; // ... to allow user to change pages again
    }
}

Ответ 8

Используйте методы UIPageViewControllerDelegate и настройте защитные меры, чтобы предотвратить создание новых просмотров страниц при обнаружении чрезмерных поворотов страниц.

  • Вы можете отключить распознаватели жестов
  • Установить "userInteraction" для отключения в UIView
  • поддерживать флаг в UIPageViewController, чтобы игнорировать дальнейший ввод, когда происходит анимация. (предупреждение об этой опции.. ios5 и ios6 имеют разные способы определения начала анимации.)

Ответ 9

Мне пришлось добавить его в viewDidAppear: чтобы он работал

    - (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];
    self.pageAnimationFinished = YES;
}

Ответ 10

Я буду пытаться игнорировать жест в UIPageViewControllers при переходе.