#ios #objective-c #uicollectionview
#iOS #objective-c #uicollectionview
Вопрос:
У меня есть контроллер collectionViewController с горизонтальным представлением коллекции, как в приложении Paper. Я добавил жест панорамирования для перехода с одного макета на другой и использовал интерактивный переход. Это хорошо работает, если вы перетаскиваете и ждете завершения анимации, но если вы перетаскиваете быстрее несколько раз и не ждете завершения или отмены анимации, приложение выдает исключение:
Assertion failure in -[UICollectionView _finishInteractiveTransitionShouldFinish:finalAnimation:], /SourceCache/UIKit_Sim/UIKit-2935.137/UICollectionView.m:2691
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'the collection was not prepared for an interactive transition. see startInteractiveTransitionToCollectionViewLayout:completion:'
Код обработчика жестов :
- (void)oneFingerGesture:(UIPanGestureRecognizer *)sender
{
if (sender.state == UIGestureRecognizerStateEnded ||
sender.state == UIGestureRecognizerStateCancelled)
{
if (self.transitionLayout.transitionProgress > 0.2) {
[self.collectionView finishInteractiveTransition];
} else {
[self.collectionView cancelInteractiveTransition];
}
}else
{
CGPoint point = [sender locationInView:sender.view];
if (sender.state == UIGestureRecognizerStateBegan amp;amp; !self.transitionLayout amp;amp; !_isInTransition)
{
invertPan = self.largeLayout == self.collectionView.collectionViewLayout;
UICollectionViewLayout *toLayout = invertPan ? self.smallLayout : self.largeLayout;
self.transitionLayout = [self.collectionView startInteractiveTransitionToCollectionViewLayout:toLayout
completion:^(BOOL completed, BOOL finish) {
self.transitionLayout = nil;
_isInTransition = NO; }];
self.initialTapPoint = point;
_isInTransition = YES;
}else if(sender.state == UIGestureRecognizerStateChanged amp;amp; self.transitionLayout amp;amp; _isInTransition)
{
CGFloat distance = _initialTapPoint.y - point.y;
if (invertPan) {
distance = -distance;
}
CGFloat dimension = self.collectionView.bounds.size.height - 200;
CGFloat progress = MAX(MIN(((distance)/ dimension), 1.0), 0.0);
[self.transitionLayout setTransitionProgress:progress];
}
}
}
Комментарии:
1. Вы пробовали пошагово выполнять отладку этим методом?
Ответ №1:
В документации указано, что вызывается finishInteractiveTransition:
и cancelInteractiveTransition:
устанавливается объект layout, к которому и из которого происходит переход, соответственно. Однако, похоже, это происходит не сразу. Таким образом, если запуск жеста и управление переходом могут происходить в быстрой последовательности, недостаточно проверить, является ли текущий макет UICollectionViewTransitionLayout или его подклассом, прежде чем вызывать один из двух методов для завершения перехода.
Я решил проблему, введя BOOL ivar (_isfinishing или cancellingtransition), чтобы избежать завершения перехода, который уже находится в процессе завершения:
if (_isFinishingOrCancellingTransition) return;
if (!self.transitionLayout) return;
if (self.collectionView.collectionViewLayout != self.transitionLayout) return;
_isFinishingOrCancellingTransition = YES;
if (self.transitionLayout.transitionProgress > 0.5)
{
[self.collectionView finishInteractiveTransition];
}
else
{
[self.collectionView cancelInteractiveTransition];
}
а затем сброс BOOL при завершении перехода:
self.transitionLayout = [self.collectionView startInteractiveTransitionToCollectionViewLayout:toLayout completion:^(BOOL completed, BOOL finish) {
self.transitionLayout = nil;
_isFinishingOrCancellingTransition = NO;
}];
Комментарии:
1. Это было не совсем то, что мне было нужно, но это определенно направило меня в правильном направлении. Спасибо!