5

我一直在努力尝试重新创建 Evernote 在 iOS 7 中使用的可伸缩集合视图,并且我真的很接近让它工作。我设法创建了一个自定义集合视图流布局,当内容偏移 y 值位于集合视图边界之外时,它会修改布局属性转换。我正在修改 layoutAttributesForElementsInRect 方法中的布局属性,它的行为与预期一样,只是当您点击滚动视图的底部时底部单元格可能会消失。将内容偏移量拉得越远,就会有越多的单元格消失。我认为细胞基本上被剪掉了。但它不会发生在顶部,我希望在两个地方都能看到相同的行为。这是我的流布局实现现在的样子。

@implementation CNStretchyCollectionViewFlowLayout
{
    BOOL        _transformsNeedReset;
    CGFloat     _scrollResistanceDenominator;
}

- (id)init
{
    self = [super init];
    if (self)
    {
        // Set up the flow layout parameters
        self.minimumInteritemSpacing = 10;
        self.minimumLineSpacing = 10;
        self.itemSize = CGSizeMake(320, 44);
        self.sectionInset = UIEdgeInsetsMake(10, 0, 10, 0);

        // Set up ivars
        _transformsNeedReset = NO;
        _scrollResistanceDenominator = 800.0f;
    }

    return self;
}

- (void)prepareLayout
{
    [super prepareLayout];
}

- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
    // Set up the default attributes using the parent implementation
    NSArray *items = [super layoutAttributesForElementsInRect:rect];

    // Compute whether we need to adjust the transforms on the cells
    CGFloat collectionViewHeight = self.collectionViewContentSize.height;
    CGFloat topOffset = 0.0f;
    CGFloat bottomOffset = collectionViewHeight - self.collectionView.frame.size.height;
    CGFloat yPosition = self.collectionView.contentOffset.y;

    // Update the transforms if necessary
    if (yPosition < topOffset)
    {
        // Compute the stretch delta
        CGFloat stretchDelta = topOffset - yPosition;
        NSLog(@"Stretching Top by: %f", stretchDelta);

        // Iterate through all the visible items for the new bounds and update the transform
        for (UICollectionViewLayoutAttributes *item in items)
        {
            CGFloat distanceFromTop = item.center.y;
            CGFloat scrollResistance = distanceFromTop / 800.0f;
            item.transform = CGAffineTransformMakeTranslation(0, -stretchDelta + (stretchDelta * scrollResistance));
        }

        // Update the ivar for requiring a reset
        _transformsNeedReset = YES;
    }
    else if (yPosition > bottomOffset)
    {
        // Compute the stretch delta
        CGFloat stretchDelta = yPosition - bottomOffset;
        NSLog(@"Stretching bottom by: %f", stretchDelta);

        // Iterate through all the visible items for the new bounds and update the transform
        for (UICollectionViewLayoutAttributes *item in items)
        {
            CGFloat distanceFromBottom = collectionViewHeight - item.center.y;
            CGFloat scrollResistance = distanceFromBottom / 800.0f;
            item.transform = CGAffineTransformMakeTranslation(0, stretchDelta + (-stretchDelta * scrollResistance));
        }

        // Update the ivar for requiring a reset
        _transformsNeedReset = YES;
    }
    else if (_transformsNeedReset)
    {
        NSLog(@"Resetting transforms");
        _transformsNeedReset = NO;
        for (UICollectionViewLayoutAttributes *item in items)
            item.transform = CGAffineTransformIdentity;
    }

    return items;
}

- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds
{
    // Compute whether we need to adjust the transforms on the cells
    CGFloat collectionViewHeight = self.collectionViewContentSize.height;
    CGFloat topOffset = 0.0f;
    CGFloat bottomOffset = collectionViewHeight - self.collectionView.frame.size.height;
    CGFloat yPosition = self.collectionView.contentOffset.y;

    // Handle cases where the layout needs to be rebuilt
    if (yPosition < topOffset)
        return YES;
    else if (yPosition > bottomOffset)
        return YES;
    else if (_transformsNeedReset)
        return YES;

    return NO;
}

@end

我还压缩了这个项目供人们试用。任何帮助将不胜感激,因为我对创建自定义集合视图布局非常陌生。这是它的链接:

https://dl.dropboxusercontent.com/u/2975688/StackOverflow/stretchy_collection_view.zip

感谢大家!

4

1 回答 1

2

我能够解决这个问题。我不确定 iOS 中是否真的存在错误,但问题是单元格实际上是在集合视图的内容视图之外被翻译的。一旦单元格被翻译得足够远,它就会被剪掉。我发现有趣的是,在非视网膜显示器的模拟器中不会发生这种情况,但在视网膜显示器上会发生,这就是为什么我觉得这实际上可能是一个错误。

考虑到这一点,目前的解决方法是通过覆盖 collectionViewContentSize 方法在集合视图的顶部和底部添加填充。完成此操作后,如果在顶部添加填充,则还需要调整单元格的布局属性,以便它们位于正确的位置。最后一步是在集合视图本身上设置 contentInset 以调整填充。留下滚动指示器插图,因为它们很好。这是我最终的集合视图控制器和自定义流布局的实现。

CNStretchyCollectionViewController.m

@implementation CNStretchyCollectionViewController

static NSString *CellIdentifier = @"Cell";

- (void)viewDidLoad
{
    // Register the cell
    [self.collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:CellIdentifier];

    // Tweak out the content insets
    CNStretchyCollectionViewFlowLayout *layout = (CNStretchyCollectionViewFlowLayout *) self.collectionViewLayout;
    self.collectionView.contentInset = layout.bufferedContentInsets;

    // Set the delegate for the collection view
    self.collectionView.delegate = self;
    self.collectionView.clipsToBounds = NO;

    // Customize the appearance of the collection view
    self.collectionView.backgroundColor = [UIColor whiteColor];
    self.collectionView.indicatorStyle = UIScrollViewIndicatorStyleDefault;
}

#pragma mark - UICollectionViewDataSource Methods

- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
    return 20;
}

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:CellIdentifier forIndexPath:indexPath];
    if ([indexPath row] % 2 == 0)
        cell.backgroundColor = [UIColor orangeColor];
    else
        cell.backgroundColor = [UIColor blueColor];

    return cell;
}

@end

CNStretchyCollectionViewFlowLayout.m

@interface CNStretchyCollectionViewFlowLayout ()

- (CGSize)collectionViewContentSizeWithoutOverflow;

@end

#pragma mark -

@implementation CNStretchyCollectionViewFlowLayout
{
    BOOL            _transformsNeedReset;
    CGFloat         _scrollResistanceDenominator;
    UIEdgeInsets    _contentOverflowPadding;
}

- (id)init
{
    self = [super init];
    if (self)
    {
        // Set up the flow layout parameters
        self.minimumInteritemSpacing = 10;
        self.minimumLineSpacing = 10;
        self.itemSize = CGSizeMake(320, 44);
        self.sectionInset = UIEdgeInsetsMake(10, 0, 10, 0);

        // Set up ivars
        _transformsNeedReset = NO;
        _scrollResistanceDenominator = 800.0f;
        _contentOverflowPadding = UIEdgeInsetsMake(100.0f, 0.0f, 100.0f, 0.0f);
        _bufferedContentInsets = _contentOverflowPadding;
        _bufferedContentInsets.top *= -1;
        _bufferedContentInsets.bottom *= -1;
    }

    return self;
}

- (void)prepareLayout
{
    [super prepareLayout];
}

- (CGSize)collectionViewContentSize
{
    CGSize contentSize = [super collectionViewContentSize];
    contentSize.height += _contentOverflowPadding.top + _contentOverflowPadding.bottom;
    return contentSize;
}

- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
    // Set up the default attributes using the parent implementation (need to adjust the rect to account for buffer spacing)
    rect = UIEdgeInsetsInsetRect(rect, _bufferedContentInsets);
    NSArray *items = [super layoutAttributesForElementsInRect:rect];

    // Shift all the items down due to the content overflow padding
    for (UICollectionViewLayoutAttributes *item in items)
    {
        CGPoint center = item.center;
        center.y += _contentOverflowPadding.top;
        item.center = center;
    }

    // Compute whether we need to adjust the transforms on the cells
    CGFloat collectionViewHeight = [self collectionViewContentSizeWithoutOverflow].height;
    CGFloat topOffset = _contentOverflowPadding.top;
    CGFloat bottomOffset = collectionViewHeight - self.collectionView.frame.size.height + _contentOverflowPadding.top;
    CGFloat yPosition = self.collectionView.contentOffset.y;

    // Update the transforms if necessary
    if (yPosition < topOffset)
    {
        // Compute the stretch delta
        CGFloat stretchDelta = topOffset - yPosition;
        NSLog(@"Stretching Top by: %f", stretchDelta);

        // Iterate through all the visible items for the new bounds and update the transform
        for (UICollectionViewLayoutAttributes *item in items)
        {
            CGFloat distanceFromTop = item.center.y - _contentOverflowPadding.top;
            CGFloat scrollResistance = distanceFromTop / _scrollResistanceDenominator;
            item.transform = CGAffineTransformMakeTranslation(0, -stretchDelta + (stretchDelta * scrollResistance));
        }

        // Update the ivar for requiring a reset
        _transformsNeedReset = YES;
    }
    else if (yPosition > bottomOffset)
    {
        // Compute the stretch delta
        CGFloat stretchDelta = yPosition - bottomOffset;
        NSLog(@"Stretching bottom by: %f", stretchDelta);

        // Iterate through all the visible items for the new bounds and update the transform
        for (UICollectionViewLayoutAttributes *item in items)
        {
            CGFloat distanceFromBottom = collectionViewHeight + _contentOverflowPadding.top - item.center.y;
            CGFloat scrollResistance = distanceFromBottom / _scrollResistanceDenominator;
            item.transform = CGAffineTransformMakeTranslation(0, stretchDelta + (-stretchDelta * scrollResistance));
        }

        // Update the ivar for requiring a reset
        _transformsNeedReset = YES;
    }
    else if (_transformsNeedReset)
    {
        NSLog(@"Resetting transforms");
        _transformsNeedReset = NO;
        for (UICollectionViewLayoutAttributes *item in items)
            item.transform = CGAffineTransformIdentity;
    }

    return items;
}

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

#pragma mark - Private Methods

- (CGSize)collectionViewContentSizeWithoutOverflow
{
    return [super collectionViewContentSize];
}

@end

CNStretchyCollectionViewFlowLayout.h

@interface CNStretchyCollectionViewFlowLayout : UICollectionViewFlowLayout

@property (assign, nonatomic) UIEdgeInsets bufferedContentInsets;

@end

实际上,我将在 Github 上完成此操作,一旦启动,我将发布该项目的链接。再次感谢大家!

于 2013-11-05T00:41:37.690 回答