Удалить колебание из UIAttachmentBehavior в UICollectionView

Я пытаюсь воссоздать поведение пружины, которое вы видите в приложении Сообщения iOS в моем UICollectionView, Как и сообщения, он будет иметь различные размеры ячеек в зависимости от размера текста. Я создал кастом UICollectionViewFlowLayout который добавляет поведение к UICollectionView однако пузырьки сообщения продолжают слегка колебаться после того, как пользователь прекратил прокрутку. Я пробовал любое количество комбинаций в length, damping а также spring значения, но колебания никогда не уходят.

После прочтения других вопросов о стеке я нашел этот комментарий

Чтобы предотвратить колебания, необходимо динамически увеличивать коэффициент демпфирования в квадратичном масштабе, когда прикрепленные виды становятся все ближе и ближе к точкам их крепления. <

Но я не совсем уверен, с чего начать реализацию чего-то подобного на том, что у меня сейчас есть. Любая помощь или руководство будут оценены.

Ниже мой код на UICollectionViewFlowLayout это создает текущий эффект.

- (void) prepareLayout {
[super prepareLayout];

CGRect originalRect = (CGRect){.origin = self.collectionView.bounds.origin, .size = self.collectionView.frame.size};
CGRect visibleRect = CGRectInset(originalRect, -50, -50);

NSArray *itemsInVisibleRectArray = [super layoutAttributesForElementsInRect:visibleRect];
NSSet *itemsIndexPathsInVisibleRectSet = [NSSet setWithArray:[itemsInVisibleRectArray valueForKey:@"indexPath"]];


NSPredicate *predicate = [NSPredicate predicateWithBlock:^BOOL(UIAttachmentBehavior *behaviour, NSDictionary *bindings) {
    BOOL currentlyVisible = [itemsIndexPathsInVisibleRectSet member:[[[behaviour items] firstObject] indexPath]] != nil;
    return !currentlyVisible;
}];
NSArray *noLongerVisibleBehaviours = [self.animator.behaviors filteredArrayUsingPredicate:predicate];

[noLongerVisibleBehaviours enumerateObjectsUsingBlock:^(id obj, NSUInteger index, BOOL *stop) {
    [self.animator removeBehavior:obj];
    [self.visibleIndexPathsSet removeObject:[[[obj items] firstObject] indexPath]];
}];


NSPredicate *newPredicate = [NSPredicate predicateWithBlock:^BOOL(UICollectionViewLayoutAttributes *item, NSDictionary *bindings) {
    BOOL currentlyVisible = [self.visibleIndexPathsSet member:item.indexPath] != nil;
    return !currentlyVisible;
}];
NSArray *newlyVisibleItems = [itemsInVisibleRectArray filteredArrayUsingPredicate:newPredicate];
CGPoint touchLocation = [self.collectionView.panGestureRecognizer locationInView:self.collectionView];



[newlyVisibleItems enumerateObjectsUsingBlock:^(UICollectionViewLayoutAttributes *item, NSUInteger idx, BOOL *stop) {
    CGPoint center = item.center;
    UIAttachmentBehavior *springBehaviour = [[UIAttachmentBehavior alloc] initWithItem:item attachedToAnchor:center];

    springBehaviour.length = 0.1f;
    springBehaviour.damping = 3.0f;
    springBehaviour.frequency = 2.8f;

    if (!CGPointEqualToPoint(CGPointZero, touchLocation)) {
        CGFloat yDistanceFromTouch = fabs(touchLocation.y - springBehaviour.anchorPoint.y);
        CGFloat xDistanceFromTouch = fabs(touchLocation.x - springBehaviour.anchorPoint.x);
        CGFloat scrollResistance = (yDistanceFromTouch + xDistanceFromTouch) / 1500.0f;

        if (self.latestDelta < 0) {
            center.y += MAX(self.latestDelta, self.latestDelta*scrollResistance);
        }
        else {
            center.y += MIN(self.latestDelta, self.latestDelta*scrollResistance);
        }
        item.center = center;
    }

    [self.animator addBehavior:springBehaviour];
    [self.visibleIndexPathsSet addObject:item.indexPath];
}];
}

1 ответ

Вы можете исправить это за 2 шага.
1. добавьте действие для поведения при инициализации, чтобы убедиться, что центр ячейки не изменяется во время анимации


springBehaviour.action = ^{
            CGPoint itemCenter = item.center;
            itemCenter.x = center.x;
            item.center = itemCenter;
        };
  1. удалить / повторно добавить поведение, когда просмотр коллекции перестает прокручиваться. Для этого вам нужно реализовать метод делегата scrollview и в этом методе удалить / повторно добавить поведения.

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
    ZZHCollectionFlowLayout *flowLayout = self.collectionView.collectionViewLayout;
    if ([flowLayout isKindOfClass:[ZZHCollectionFlowLayout class]])
    {
        [flowLayout removeAnimationBehavior];
    }
    else
    {
        // Your NSAssertionHandler
    }
}

- (void)removeAnimationBehavior
{
    NSArray *behaviors = self.dynamicAnimator.behaviors;
    [self.dynamicAnimator removeAllBehaviors];
    for (UIDynamicBehavior *obj in behaviors)
    {
        [self.dynamicAnimator addBehavior:obj];
    }
}

Кстати, если есть способ изменения демпфирования, чтобы исправить, хотел бы услышать это!