带有CustomFlowLayout的UICollectionView如何将滚动限制为每个滚动仅一页? [英] UICollectionView with CustomFlowLayout How to restricts scroll to only one page per scroll?

查看:95
本文介绍了带有CustomFlowLayout的UICollectionView如何将滚动限制为每个滚动仅一页?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经在我的iOS应用中实现了customFlowLayout.我已经将targetContentOffsetForProposedContentOffset:withScrollingVelocity子类化为 子类化UICollectionViewFlowLayout.现在我的问题是,当用户滚动collectionview时,它必须仅滚动到下一个索引.现在,它随机滚动.
因此,任何人都知道我如何制作滚动条,每个滚动条只能包含一项.
以下是我的代码.

I have implemented customFlowLayout in my iOS App. And I have subclassed the targetContentOffsetForProposedContentOffset:withScrollingVelocity with subclassing UICollectionViewFlowLayout. Now my issue is when user scrolls the collectionview it must scroll to only next index. Right now it scrolled randomly.
So anyone have any idea that how can I make the scroll restricts to only one item per scroll.
Following is my code.

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

推荐答案

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

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...

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

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.

这篇关于带有CustomFlowLayout的UICollectionView如何将滚动限制为每个滚动仅一页?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆