32

介绍

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

最相关的代码位于问题底部,以便快速访问。您可以在 BitBucket下载源代码的 zip文件或将项目作为Mercurial 存储库获取。该项目现在包含以下答案中的修复。如果您想要最初提供的损坏版本,则将其标记为“initial-buggy-version”

该项目是一个极简的概念证明/尖峰,用于评估效果是否可行,所以它相当轻巧!

所需效果

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

在我的概念证明中,单个单元格没有调整大小,它们只是重新定位得更近或更远。这是故意的:我认为验证这个想法的可行性并不重要。

这是屏幕截图,显示了当前 App 的缩小和放大效果:

放大图像 缩小的图像

当前实施

我正在使用UICollectionView带有自定义UICollectionViewLayout子类的 a 。布局UICollectionViewCells在屏幕中间以一个漂亮的摆动正弦波定位。每个UICollectionViewCell都只是一个容纳UILabel行的容器indexPath

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

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

如果没有进一步考虑,缩放将从内容的顶部开始,而不是触摸手势的中心。在捏合期间调整UICollectionView'contentOffset属性以提供此功能。

手势识别器还需要适应捏合时发生的拖动。这也可以通过更改UICollectionView's来处理contentOffset。一些额外的代码允许触摸手势的中心点随着手指添加到手势/从手势中移除而改变。

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

我添加了代码以UICollectionView在捏手势期间禁用 ' 的内置滚动,但这似乎没有太大区别。我试图用内置的gestureRecognizer:shouldRequireFailureOfGestureRecognizer:来使我的失败,但这似乎完全停止了我的捏识别器的工作。我不知道这是我的愚蠢,还是iOS中的错误。UIPinchGestureRecognizerUIPanGestureRecognizer

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

什么有效

工作位工作得很好。您可以上下拖动表格。在拖动过程中,您可以添加手指并开始捏合,然后松开手指并继续拖动,然后添加并捏合等。这一切都非常流畅。在最初的 iPhone 5 上,它顺利地支持在屏幕上显示超过 200 个视图的捏合和平移。

什么不起作用 1

如果您试图在视图的顶部或底部出现在屏幕上时进行捏合,这一切都会变得有点疯狂。

  • 在滚动时,允许拖动视图,以便将其拉到可见内容之外(我想要,因为它是 iOS 上数据列表的标准行为)。
  • 然而,在比例变化时,视图会被捕捉回来,以便内容被夹在屏幕上(我不希望这种情况发生)。

这两个在捏合手势时互相打架,这使得内容上下剧烈闪烁(我绝对不想要!)。

什么不起作用 2

如果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
4

1 回答 1

18

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

对上述代码的一些非常小的调整已经解决了问题中的什么不起作用 1什么不起作用 2

我在viewDidLoadmy 的方法中添加了以下几行UICollectionViewController

[collectionView setMinimumZoomScale: 0.25];
[collectionView setMaximumZoomScale: 4];

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

图片缩小了 图片放大

在缩放过程中,圆圈的视图不会重新绘制,而只是从它们的缩放前大小进行插值。重绘被推迟到缩放完成。这是放大几次后的样子:

变焦期间

在后台线程中进行缩放期间的重绘会很棒,这样伪影就不那么明显了,但这远远超出了这个问题的范围,我也没有研究过。

您可以在 Bit Bucket 上找到带有修复程序的整个项目,以便在那里获取文件

不好的部分——我不知道它为什么起作用

我希望在回答了这个问题后,我会对UIScrollView缩放有很多新的确定性。我不。

从我读到的关于 UIScrollView 的内容来看,这个“修复”应该没有任何区别,而且它应该已经开始工作了。

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

于 2014-01-08T13:05:21.633 回答