5

我已经customFlowLayout在我的 iOS 应用程序中实现了。而且我已经将targetContentOffsetForProposedContentOffset:withScrollingVelocitywith subclassing 子类化了UICollectionViewFlowLayout。现在我的问题是当用户滚动时,collectionview它必须滚动到下一个索引。现在它随机滚动。
因此,任何人都知道如何使滚动限制为每个滚动仅一个项目。
以下是我的代码。

#pragma mark - UICollectionViewLayout (UISubclassingHooks)

- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity {
  CGSize collectionViewSize = self.collectionView.bounds.size;
  CGFloat proposedContentOffsetCenterX = proposedContentOffset.x + collectionViewSize.width / 2;
  CGRect proposedRect = CGRectMake(proposedContentOffset.x, 0, collectionViewSize.width, collectionViewSize.height);
  UICollectionViewLayoutAttributes *candidateAttributes;
  for (UICollectionViewLayoutAttributes *attributes in [self layoutAttributesForElementsInRect:proposedRect]) {
    if (attributes.representedElementCategory != UICollectionElementCategoryCell) continue;
    if (!candidateAttributes) {
      candidateAttributes = attributes;
      continue;
    }
    if (fabs(attributes.center.x - proposedContentOffsetCenterX) < fabs(candidateAttributes.center.x - proposedContentOffsetCenterX)) {
      candidateAttributes = attributes;
    }
  }

  proposedContentOffset.x = candidateAttributes.center.x - self.collectionView.bounds.size.width / 2;

  CGFloat offset = proposedContentOffset.x - self.collectionView.contentOffset.x;

  if ((velocity.x < 0 && offset > 0) || (velocity.x > 0 && offset < 0)) {
    CGFloat pageWidth = self.itemSize.width + self.minimumLineSpacing;
    proposedContentOffset.x += velocity.x > 0 ? pageWidth : -pageWidth;
  }

  return proposedContentOffset;
}

- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds {
  return YES;
}

- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {
  if (!self.scaleItems) return [super layoutAttributesForElementsInRect:rect];

  NSArray *attributesArray = [[NSArray alloc] initWithArray:[super layoutAttributesForElementsInRect:rect] copyItems:YES];

  CGRect visibleRect = (CGRect){self.collectionView.contentOffset, self.collectionView.bounds.size};
  CGFloat visibleCenterX = CGRectGetMidX(visibleRect);

  [attributesArray enumerateObjectsUsingBlock:^(UICollectionViewLayoutAttributes *attributes, NSUInteger idx, BOOL *stop) {
    CGFloat distanceFromCenter = visibleCenterX - attributes.center.x;
    CGFloat absDistanceFromCenter = MIN(ABS(distanceFromCenter), self.scalingOffset);
    CGFloat scale = absDistanceFromCenter * (self.minimumScaleFactor - 1) / self.scalingOffset + 1;
    attributes.transform3D = CATransform3DScale(CATransform3DIdentity, scale, scale, 1);
  }];

  return attributesArray;
}
4

1 回答 1

1

Your code looks like it should scroll nicely based on the user request. i.e. if they scroll quickly it will skip a number of items and land nicely on a later item, if they scroll slowly it will proceed to the next or return to the previous item nicely depending on the scroll distance. But, this isn't what you say you want.

What you want may not be very nice to use when the user tries to scroll quickly...

Anyway, to get what you want you basically want to use proposedContentOffset only to determine the scroll direction (is it greater than or less than the current content offset).

Now, once you have that you can get the layout attributes for the items at the next or previous page (rather than the current code which may get attributes many pages away). This is the current offset + or - the view width.

The rest of your code then stays the same. Ignoring the scroll direction this is something like:

- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity
{
    CGSize collectionViewSize = self.collectionView.bounds.size;
    CGFloat width = collectionViewSize.width;
    CGFloat halfWidth = width * 0.5;

    CGFloat direction = (proposedContentOffset.x > self.collectionView.contentOffset.x ? 1 : 0);
    CGFloat pageOffsetX = 250.0 * floor(self.collectionView.contentOffset.x / 250.0); 
    CGFloat proposedContentOffsetCenterX = pageOffsetX + (width * direction);
    CGRect proposedRect = CGRectMake(proposedContentOffsetCenterX, 0, collectionViewSize.width, collectionViewSize.height);

    UICollectionViewLayoutAttributes *candidateAttributes;

    for (UICollectionViewLayoutAttributes *attributes in [self layoutAttributesForElementsInRect:proposedRect]) {
        if (attributes.representedElementCategory != UICollectionElementCategoryCell) continue;

        candidateAttributes = attributes;
        break;
    }

    proposedContentOffset.x = candidateAttributes.center.x - halfWidth;

//     CGFloat offset = proposedContentOffset.x - self.collectionView.contentOffset.x;
//     
//     if ((velocity.x < 0 && offset > 0) || (velocity.x > 0 && offset < 0)) {
//         CGFloat pageWidth = self.itemSize.width + self.minimumLineSpacing;
//         proposedContentOffset.x += velocity.x > 0 ? pageWidth : -pageWidth;
//     }

    return proposedContentOffset;
}

I've commented out the part at the bottom as it shouldn't be required for the initial version. Test it out with a simple version first and then elaborate if you need more control in edge cases.

于 2016-03-31T08:47:12.650 回答