将缩放缩放添加到UICollectionView

时间:2014-01-03 02:20:45

标签: ios uicollectionview pinchzoom uicollectionviewlayout uipinchgesturerecognizer

简介

我将描述我想要达到的效果,然后我会详细介绍我目前是如何尝试实现这一点以及它的行为有什么问题。它代表。我还会提到另一种我看过的但根本无法完成工作的方法。

最相关的代码内嵌在问题的底部,以便快速访问。您可以 Download a zip of the source 或在BitBucket上将项目作为 Mercurial Repository 获取。该项目现在包含以下答案的修复程序。如果你想要最初提供的破碎版本,它会被标记为" initial-buggy-version"

该项目是概念/尖峰的最小证明,用于评估效果是否可行,因此它非常简单明了!

期望效果

应用程序将显示构成垂直表的大量离散信息行。该表将由用户垂直滚动。这是UITableView的标准行为,您也可以使用UICollectionView。但是,应用程序还必须支持缩放缩放。当你在桌子上捏缩放时,所有的线条应该挤压在一起。当你伸展时,所有的线应该分开。

在我的概念证明中,单个细胞没有调整大小,它们只是重新定位在一起或更远。这是故意的:我不相信验证这个想法的可行性至关重要。

此处的屏幕抓取显示当前应用看起来如何缩小和放大:

Zoomed in image Zoomed out image

当前实施

我使用带有自定义UICollectionView子类的UICollectionViewLayout。布局将UICollectionViewCells放置在屏幕中间的一个漂亮的摇摆正弦波中。每个UICollectionViewCell只是UILabel持有indexPath行的容器。

UICollectionViewLayout子类有一个参数来设置它描述的每个单元格与UICollectionView之间的垂直间距,并且调整它可以根据需要对表格进行压扁或拉伸。

我的UICollectionViewController子类有一个UIPinchGestureRecognizer。当识别器检测到比例变化时,UICollectionView布局中的垂直单元间距相应地改变。

如果不进一步考虑,缩放将从内容的顶部发生,而不是触摸手势的中心。在捏合期间调整UICollectionView的{​​{1}}属性以提供此功能。

手势识别器还需要适应捏时发生的拖拽。这也可以通过更改contentOffset' UICollectionView来解决。一些附加代码允许触摸手势的中心点随着手指被添加到手势或从手势移除而改变。

请注意,作为contentOffset子类的UICollectionView有自己的UIScrollView,它与我添加的UIPanGestureRecognizer进行交互。我不确定这是否会导致问题。

我添加了代码以在我的捏合手势中禁用UIPinchGestureRecogniser内置的滚动功能,但这似乎并没有太大的区别。我尝试使用UICollectionView让我的gestureRecognizer:shouldRequireFailureOfGestureRecognizer:失败内置UIPinchGestureRecognizer,但这似乎阻止了我的捏合识别器工作。我不知道这是我愚蠢,还是iOS中的错误。

如前所述,当前UIPanGestureRecognizer s未调整大小。他们只是重新定位。这是故意的。我不认为验证这个概念很重要。

什么有用

工作位工作得很好。您可以上下拖动表格。在拖动过程中,你可以添加一个手指并开始捏,然后释放手指并继续拖动,然后添加和捏合等。它都非常流畅。在原装iPhone 5上,它可以平滑地支持捏合和平移。屏幕上有200个视图。

什么不起作用1

如果您在屏幕的顶部或底部出现时尝试捏合,那么一切都会有点疯狂。

  • 在滚动时,允许拖动视图,使其超出可见内容(我想要的,因为它是iOS上数据列表的标准行为)。
  • 然而,在比例更改时,视图会被快照回来,以便内容被限制在屏幕上(我不希望这种情况发生)。

这两个人在捏合手势期间互相争斗,这使得内容剧烈地上下闪烁(我绝对不想要!)。

什么不起作用2

如果您在滚动时放开,UICollectionViewCell的默认滚动会减速,并且当您向外滚动时,内容会平滑地反弹回内容。目前还没有处理过这些问题。

  • 如果您在滚动时释放捏合手势,它就会停止。
  • 如果您使用捏合手势滚动内容然后释放,它会保持原样并且不会反弹。然后,当您再次开始滚动时,它会跳回内容。

我尝试但无法开始工作的事情

UICollectionView,如果UICollectionView设置正确以支持缩放,那么UIScrollView应该内置UIPinchGestureRecogniser。我想知道我是否可以利用这个而不是拥有自己的UIPinchGestureRecogniser。我尝试通过设置最小和最大比例以及添加我的控制器的捏合处理程序来设置它。但是,我真的不明白我应该从viewForZoomingInScrollView:的实现中返回什么,所以我只是用[[UIView alloc] initWithFrame: [[self collectionView] bounds]]创建一个虚拟视图。它使滚动视图"崩溃"到一条线,这不是我之后的事情!

最后(在代码之前)

这是一个很长的问题,所以感谢您阅读。如果你能帮忙解答,请多谢一点。对不起,如果我说的或添加的内容很多,那就无关紧要了!

视图控制器的代码

//  STViewController.m
#import "STViewController.h"
#import "STDataColumnsCollectionViewLayout.h"
#import "STCollectionViewLabelCell.h"

@interface STViewController () <UIGestureRecognizerDelegate>
@property (nonatomic, assign) CGFloat pinchStartVerticalPeriod;
@property (nonatomic, assign) CGFloat pinchNormalisedVerticalPosition;
@property (nonatomic, assign) NSInteger pinchTouchCount;
-(void) handlePinch: (UIPinchGestureRecognizer *) pinchRecogniser;
@end

@implementation STViewController

-(void) viewDidLoad
{
  [[self collectionView] registerClass: [STCollectionViewLabelCell class] forCellWithReuseIdentifier: [STCollectionViewLabelCell className]];

  UICollectionView *const collectionView = [self collectionView];
  [collectionView setAllowsSelection: NO];

  [_pinchRecogniser addTarget: self action: @selector(handlePinch:)];
  [_pinchRecogniser setDelegate: self];
  [_pinchRecogniser setCancelsTouchesInView:YES];
  [[self view] addGestureRecognizer: _pinchRecogniser];
}

#pragma mark -

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

-(UICollectionViewCell*) collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
  STCollectionViewLabelCell *const cell = [[self collectionView] dequeueReusableCellWithReuseIdentifier: [STCollectionViewLabelCell className] forIndexPath: indexPath];
  [[cell label] setText: [NSString stringWithFormat: @"%d", [indexPath row]]];
  return cell;
}

#pragma mark -

-(BOOL) gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
  return YES;
}

#pragma mark -

-(void) handlePinch: (UIPinchGestureRecognizer *) pinchRecogniser
{
  UICollectionView *const collectionView = [self collectionView];
  STDataColumnsCollectionViewLayout *const layout = (STDataColumnsCollectionViewLayout *)[self collectionViewLayout];

  if(([pinchRecogniser state] == UIGestureRecognizerStateBegan) || ([pinchRecogniser numberOfTouches] != _pinchTouchCount))
  {
    const CGFloat normalisedY = [pinchRecogniser locationInView: collectionView].y / [layout collectionViewContentSize].height;
    _pinchNormalisedVerticalPosition = normalisedY;
    _pinchTouchCount = [pinchRecogniser numberOfTouches];
  }

  switch ([pinchRecogniser state])
  {
    case UIGestureRecognizerStateBegan:
    {
      NSLog(@"Began");
      _pinchStartVerticalPeriod = [layout verticalPeriod];
      [collectionView setScrollEnabled: NO];
      break;
    }

    case UIGestureRecognizerStateChanged:
    {
      NSLog(@"Changed");
      STDataColumnsCollectionViewLayout *const layout = (STDataColumnsCollectionViewLayout *)[self collectionViewLayout];
      const CGFloat newVerticalPeriod = _pinchStartVerticalPeriod * [pinchRecogniser scale];
      [layout setVerticalPeriod: newVerticalPeriod];
      [[self collectionViewLayout] invalidateLayout];

      const CGPoint dragCenter = [pinchRecogniser locationInView: [collectionView superview]];
      const CGFloat currentY = _pinchNormalisedVerticalPosition * [layout collectionViewContentSize].height;
      [collectionView setContentOffset: CGPointMake(0, currentY - dragCenter.y) animated: NO];
    }

    case UIGestureRecognizerStateEnded:
    case UIGestureRecognizerStateCancelled:
    {
      [collectionView setScrollEnabled: YES];
    }

    default:
      break;
  }
}

@end

1 个答案:

答案 0 :(得分:16)

好的一点 - 如何让它发挥作用

上述代码的一些非常小的调整已经解决了什么不起作用&amp;问题中什么行不通2

我已将以下几行添加到viewDidLoad的{​​{1}}方法中:

UICollectionViewController

我还更新了示例项目,以便视图由小圆圈组成,而不是文本标签。放大和缩小时,会调整大小。这是现在的样子(缩小和放大):

Image zoomed out Image zoomed in

在缩放过程中,圆圈的视图不会重绘,而只是从缩放前的大小进行插值。重绘被推迟到缩放完成。这里是对几次放大后的看法的捕捉:

During zoom

在缩放期间重绘会在后台线程中进行重绘会很好,这样人工制品就不那么明显了,但这远远超出了这个问题的范围,而且我还没有对它进行过处理。

您可以在Bit Bucket上找到整个项目,包含修复程序,以便{... 3}}在那里。

坏部分 - 我不知道它为什么会起作用

我希望通过回答这个问题,我会对[collectionView setMinimumZoomScale: 0.25]; [collectionView setMaximumZoomScale: 4]; 缩放有很多新的确定性。我没有。

从我读过的关于UIScrollView的内容来看,这个“修复”应该没有任何区别,它应该已经在第一时间起作用了。

UIScrollView不应该启用滚动,直到你给它一个实现UIScrollView的委托,我没有这样做。