缩放整个UICollectionView

9
我有一个iPad应用程序,其中我使用了UICollectionView,每个UICollectionViewCell仅包含一个UIImage。目前,我每页显示9张图片(3行* 3列),有多个页面。
我想使用Pinch手势对整个UICollectionView进行缩放,以增加/减少每页显示的行/列数,并在Pinch手势期间具有美丽的缩放动画!
目前,我已在我的UICollectionView上添加了Pinch手势。我捕获Pinch手势事件以使用比例因子计算行/列数,如果已更改,则使用以下方式更新完整的UICollectionView:
[_theCollectionView performBatchUpdates:^{
     [_theCollectionView deleteSections:[NSIndexSet indexSetWithIndex:0]];
     [_theCollectionView insertSections:[NSIndexSet indexSetWithIndex:0]];
 } completion:nil];

它可以工作,但在过渡期间我没有流畅的动画。

有什么想法吗?UICollectionView继承自UIScrollView,是否有可能重用UIScrollView的捏合手势功能来实现我的目标?

3个回答

31

我假设你正在使用默认的UICollectionViewDelegateFlowLayout,对吗?那么请确保你根据代理方法作出相应响应,并在收到捏合手势时简单地使布局失效。

例如,如果我想在捏合时调整每个项目的大小:

@interface ViewController () <UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout>

@property (nonatomic,assign) CGFloat scale;
@property (nonatomic,weak)   IBOutlet UICollectionView *collectionView;

@end

@implementation ViewController
- (void)viewDidLoad
{
    [super viewDidLoad];

    self.scale = 1.0;

    [self.collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"cell"];

    UIPinchGestureRecognizer *gesture = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(didReceivePinchGesture:)];
    [self.collectionView addGestureRecognizer:gesture];

}

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
    return CGSizeMake(50*self.scale, 50*self.scale);
}

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

    if (gesture.state == UIGestureRecognizerStateBegan)
    {
        scaleStart = self.scale;
    }
    else if (gesture.state == UIGestureRecognizerStateChanged)
    {
        self.scale = scaleStart * gesture.scale;
        [self.collectionView.collectionViewLayout invalidateLayout];
    }
}

self.scale属性只是为了展示,你可以将同样的概念应用到任何其他属性中,这不需要beginUpdates/endUpdates,因为用户自己掌握着缩放的时间。

这里有一个运行中的项目,如果您想看它实际运行的效果。


+1 谢谢!我已经查看了你的演示,它非常有用。你能指导一下我们如何使用漂亮的动画展示这些转换吗?我是一个UICollectionView的新手。 - Yogi
1
很棒的答案,不知道你是否有任何想法,如果布局更加“固定”,而你想要缩放/放大和平移(不是移动项目)。例如,想象一张地图,其中地图上的项目是UICollectionCells,瓦片可能是可重用视图或类似的东西。 - MobileVet
1
@MobileVet 我的论文中,我编写了一个自定义的UICollectionViewLayout,它创建了类似地图的东西,这是完全可能的。如果您使用比例因子使集合视图布局报告的内容大小更大,则可以同时进行缩放和平移。 - Can
那不小心会使得内容比它实际需要的要更加庞大,对吧?:/ - fatuhoku
@fatuhoku 嗯,是的。你有什么问题吗? - Can

1
抱歉我的问题有点简单,我已经找到了解决方案,非常简单。在我的`PinchGesture`回调函数中,我只需要执行以下操作:
void (^animateChangeWidth)() = ^() {
    _theFlowLayout.itemSize = cellSize;
};

[UIView transitionWithView:self.theCollectionView 
                  duration:0.1f 
                   options:UIViewAnimationOptionCurveLinear 
                animations:animateChangeWidth 
                completion:nil];

我的UICollectionView的所有单元格都成功更改,并且有一个漂亮的transition


4
处理缩放的正确方法不是这样做的,你基本上是在强制重绘整个collectionView,而该功能已经通过布局失效支持 [collectionView.collectionViewLayout invalidateLayout]。我留下了一个更详细的解释和示例的答案。 - Can
请问,您能否在此添加更多细节? - sebastien

0
对于Xamarin.iOS开发者,我找到了这个解决方案:在主视图中添加一个UIScrollView元素,并将UICollectionView作为UIScrollView的元素添加进去。然后为UIScrollView创建一个缩放委托。
MainScrollView = new UIScrollView(new CGRect(View.Frame.X, View.Frame.Y, size.Width, size.Height));

        
        _cellReuseId = GenCellReuseId();

        _contentScroll = new UICollectionView(new CGRect(View.Frame.X, View.Frame.Y, size.Width, size.Height), new InfiniteScrollCollectionLayout(size.Width, size.Height));
        
        _contentScroll.AllowsSelection = true;
        _contentScroll.ReloadData();


        _contentScroll.Center = MainScrollView.Center;
        _contentScroll.Frame = new CGRect(_contentScroll.Frame.X, _contentScroll.Frame.Y - 32, _contentScroll.Frame.Width, _contentScroll.Frame.Height);
        MainScrollView.ContentSize = _contentScroll.ContentSize;
        MainScrollView.AddSubview(_contentScroll);
        MainScrollView.MaximumZoomScale = 4f;
        MainScrollView.MinimumZoomScale = 1f;
        MainScrollView.BouncesZoom = true;
        MainScrollView.ViewForZoomingInScrollView += (UIScrollView sv) =>
        {
            if (_contentScroll.Frame.Height < sv.Frame.Height && _contentScroll.Frame.Width < sv.Frame.Width)
            {
                _contentScroll.Center = MainScrollView.Center;
                _contentScroll.Frame = new CGRect(_contentScroll.Frame.X, _contentScroll.Frame.Y - 64, _contentScroll.Frame.Width, _contentScroll.Frame.Height);
                _contentScroll.BouncesZoom = true;
                _contentScroll.AlwaysBounceHorizontal = false;
            }
            return _contentScroll;
};

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接