Переход UIPageViewController 'Несбалансированные вызовы для перехода к началу/концу отображения для'
Когда я перемещаюсь через UIPageViewController
быстрее, чем его переходная анимация, я получаю 'Unbalanced calls to begin/end appearance transitions for <MyDataViewController>
', и одно из двух представлений в ландшафте не отображается, пока я не попытаюсь перевернуть страницу.
У кого-нибудь есть идея решить эту ошибку?
Ответы
Ответ 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 при переходе.