我将描述我想要达到的效果,然后我会详细介绍我目前是如何尝试实现这一点以及它的行为有什么问题。它代表。我还会提到另一种我看过的但根本无法完成工作的方法。
最相关的代码内嵌在问题的底部,以便快速访问。您可以 Download a zip of the source 或在BitBucket上将项目作为 Mercurial Repository 获取。该项目现在包含以下答案的修复程序。如果你想要最初提供的破碎版本,它会被标记为" initial-buggy-version"
该项目是概念/尖峰的最小证明,用于评估效果是否可行,因此它非常简单明了!
应用程序将显示构成垂直表的大量离散信息行。该表将由用户垂直滚动。这是UITableView
的标准行为,您也可以使用UICollectionView
。但是,应用程序还必须支持缩放缩放。当你在桌子上捏缩放时,所有的线条应该挤压在一起。当你伸展时,所有的线应该分开。
在我的概念证明中,单个细胞没有调整大小,它们只是重新定位在一起或更远。这是故意的:我不相信验证这个想法的可行性至关重要。
此处的屏幕抓取显示当前应用看起来如何缩小和放大:
我使用带有自定义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个视图。
如果您在屏幕的顶部或底部出现时尝试捏合,那么一切都会有点疯狂。
这两个人在捏合手势期间互相争斗,这使得内容剧烈地上下闪烁(我绝对不想要!)。
如果您在滚动时放开,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
答案 0 :(得分:16)
上述代码的一些非常小的调整已经解决了什么不起作用&amp;问题中什么行不通2 。
我已将以下几行添加到viewDidLoad
的{{1}}方法中:
UICollectionViewController
我还更新了示例项目,以便视图由小圆圈组成,而不是文本标签。放大和缩小时,会调整大小。这是现在的样子(缩小和放大):
在缩放过程中,圆圈的视图不会重绘,而只是从缩放前的大小进行插值。重绘被推迟到缩放完成。这里是对几次放大后的看法的捕捉:
在缩放期间重绘会在后台线程中进行重绘会很好,这样人工制品就不那么明显了,但这远远超出了这个问题的范围,而且我还没有对它进行过处理。
您可以在Bit Bucket上找到整个项目,包含修复程序,以便{... 3}}在那里。
我希望通过回答这个问题,我会对[collectionView setMinimumZoomScale: 0.25];
[collectionView setMaximumZoomScale: 4];
缩放有很多新的确定性。我没有。
从我读过的关于UIScrollView的内容来看,这个“修复”应该没有任何区别,它应该已经在第一时间起作用了。
UIScrollView
不应该启用滚动,直到你给它一个实现UIScrollView
的委托,我没有这样做。