【问题标题】:UICollectionView with CustomFlowLayout How to restricts scroll to only one page per scroll?UICollectionView 与 CustomFlowLayout 如何将滚动限制为每次滚动仅一页?
【发布时间】:2016-06-29 01:56:16
【问题描述】:

我在我的 iOS 应用中实现了customFlowLayout。我已经将targetContentOffsetForProposedContentOffset:withScrollingVelocity 子类化为 子类化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;
}

【问题讨论】:

  • 你没有为此目的使用分页?
  • 我使用了分页,但仍然无法正常工作/

标签: ios objective-c scroll uicollectionview scroll-paging


【解决方案1】:

您的代码看起来应该可以根据用户请求很好地滚动。即,如果他们快速滚动,它将跳过许多项目并很好地落在后面的项目上,如果他们滚动缓慢,它将继续到下一个或很好地返回到前一个项目,具体取决于滚动距离。但是,这不是你说的你想要的。

当用户尝试快速滚动时,您想要的内容可能不太好用......

无论如何,要获得您想要的内容,您基本上只想使用proposedContentOffset 来确定滚动方向(它是大于还是小于当前内容偏移量)。

现在,一旦你有了它,你就可以在下一页或上一页获取项目的布局属性(而不是当前代码可能会在很多页之外获取属性)。这是当前偏移量 + 或 - 视图宽度。

然后您的其余代码保持不变。忽略滚动方向是这样的:

- (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;
}

我已经注释掉了底部的部分,因为初始版本不需要它。先用一个简单的版本进行测试,如果您需要在边缘情况下进行更多控制,再进行详细说明。

【讨论】:

  • 它在什么方面不起作用(请记住我在我的 iPad 上输入了上面的代码) - 要点是不使用建议的停止点并使用当前内容偏移量
  • 这样做我的单元格没有居中。
  • 什么时候?当一个单元格被聚焦时,它会滚动到视图中,因此如果大小和项目间距设置得当,它将移动到中心。如果它没有完全移动,您可以强制内容偏移。
  • 实际上每次我想在我向右滚动时滚动+240,当我向左滚动时滚动-240,但不知何故它不起作用
  • 如果你有空我们可以聊聊吗
猜你喜欢
  • 2016-12-25
  • 1970-01-01
  • 2013-03-09
  • 1970-01-01
  • 2012-09-27
  • 2020-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-07-06
相关资源
最近更新 更多