如何在缩放UICollectionView时在屏幕上保留相同的集合视图单元格?

时间:2015-01-17 21:30:10

标签: ios objective-c uicollectionview uicollectionviewlayout

我有一个UICollectionView,它使用UICollectionViewLayout子类进行布局。布局是一个简单的网格。

当我放大或缩小集合视图时,屏幕上单元格的位置会发生变化。在某些情况下,当放大时,细胞完全离开屏幕。我使用捏合手势识别器缩放单元格,该识别器将x和y缩放值发送到布局类,然后使布局无效。

随着细胞变大,它们会移动,因为它们的原点是相对于集合视图的0,0位置计算的。

我希望能够缩放集合视图,同时让屏幕上最初的单元格保持不变。一个好的解决方案是让屏幕中心的单元格随着它变大而保持在中心位置。中心细胞周围的细胞会长大,这可能会将它们推离屏幕。

我已经尝试调整集合视图的内容偏移量,但我没有达到我想要的效果。我不太清楚如何计算它的新值,并且我已经了解到invalidateLayout引起的更改不会立即发生。

我为集合视图的内容大小尝试了一个键值观察器,但这导致了口吃,因为KVO方法的变化在原始缩放后很好地发生了。

我还使用了scrollToItemAtIndexPath,但是我的完整应用程序中的代码不能保证在屏幕的正中心有一个单元格。这个解决方案对我来说不太理想。

以下是捏合识别器将更改发送到布局类的代码:

    [self.gridLayout updateCellWidthScale:xScale];
    [self.gridLayout updateCellHeightScale:yScale];

    [self.gridLayout invalidateLayout];

这是布局类中的代码 (numberOfRows和numberOfColumns都设置为20):

-(id)initWithNumberOfRows:(NSUInteger)numberOfRows
       andNumberOfColumns:(NSUInteger)numberOfColumns
{
    self = [super init];
    if (self)
    {
        _numberOfRows = numberOfRows;
        _numberOfColumns = numberOfColumns;

        _cellWidth = 80.0f;
        _cellHeight = 80.0f;

        _cellWidthScale = 1.0f;
        _cellHeightScale = 1.0f;
    }
    return self;
}

-(void)updateCellWidthScale:(CGFloat)newWidthScale
{
    self.cellWidthScale *= newWidthScale;
}

-(void)updateCellHeightScale:(CGFloat)newHeightScale
{
    self.cellHeightScale *= newHeightScale;
}

-(CGSize)collectionViewContentSize
{
    CGSize returnValue = CGSizeMake(self.numberOfColumns * self.cellWidth * self.cellWidthScale,
                                    self.numberOfRows * self.cellHeight * self.cellHeightScale);
    return returnValue;
}

- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)path
{
    UICollectionViewLayoutAttributes* attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:path];
    CGRect rect = [self frameForItemAtIndexPath:path];

    attributes.size = CGSizeMake(rect.size.width, rect.size.height);
    attributes.center = CGPointMake(rect.origin.x + (0.5f * rect.size.width),
                                    rect.origin.y + (0.5f * rect.size.height));

    return attributes;
}

-(NSArray*)layoutAttributesForElementsInRect:(CGRect)rect
{
    NSMutableArray *returnValue = [[NSMutableArray alloc] init];

    for (NSInteger i=0; i < self.numberOfRows; i++)
    {
        for (NSInteger j=0; j < self.numberOfColumns; j++)
        {
            NSIndexPath* indexPath = [NSIndexPath indexPathForItem:j inSection:i];
            CGRect frame = [self frameForItemAtIndexPath:indexPath];

            if (CGRectIntersectsRect(frame, rect))
            {
                [returnValue addObject:[self layoutAttributesForItemAtIndexPath:indexPath]];
            }
        }
    }

    return returnValue;
}

- (CGRect)frameForItemAtIndexPath:(NSIndexPath *)indexPath
{
    CGRect returnValue = CGRectMake(indexPath.section * self.cellWidth * self.cellWidthScale,
                                    indexPath.row * self.cellHeight * self.cellHeightScale,
                                    self.cellWidth * self.cellWidthScale,
                                    self.cellHeight * self.cellHeightScale);

    return returnValue;
}

1 个答案:

答案 0 :(得分:1)

您需要将collectionView contentOffset设置为开始缩放之前的值乘以手势比例。 您的捏合识别器方法应如下所示(您需要添加更多代码以在达到MAXIMUM_SCALE或MINIMUM_SCALE时停止更改contentOffset。)

- (void)didReceivePinchGesture:(UIPinchGestureRecognizer*)gesture
{
    static CGFloat scaleStart;
    static CGPoint p;

    if (gesture.state == UIGestureRecognizerStateBegan)
    {
        scaleStart = self.scale;
        p = self.collectionView.contentOffset;   
    }
    else if (gesture.state == UIGestureRecognizerStateChanged)
    {
        CGFloat tempScale = scaleStart * gesture.scale;
        if (tempScale < MINMUM_SCALE)
        {
            self.scale = MINMUM_SCALE;
        }
        else if (tempScale > MAXIMUM_SCALE)
        {
            self.scale = MAXIMUM_SCALE;
        }
        else
        {
            self.scale = tempScale ;
        }

        dispatch_async(dispatch_get_main_queue(), ^{

            [self.collectionView.collectionViewLayout invalidateLayout];

            self.collectionView.contentOffset = CGPointMake(0, p.y * gesture.scale);
        });
    }
}