CAScrollLayer
对于一个未转换的图层,它的bounds
和它的frame
是一样的,frame
属性是由bounds
属性自动计算而出的,所以更改任意一个值都会更新其他值。
但是如果你只想显示一个大图层里面的一小部分呢。比如说,你可能有一个很大的图片,你希望用户能够随意滑动,或者是一个数据或文本的长列表。在一个典型的iOS应用中,你可能会用到UITableView
或是UIScrollView
,但是对于独立的图层来说,什么会等价于刚刚提到的UITableView
和UIScrollView
呢?
在第二章中,我们探索了图层的contentsRect
属性的用法,它的确是能够解决在图层中小地方显示大图片的解决方法。但是如果你的图层包含子图层那它就不是一个非常好的解决方案,因为,这样做的话每次你想『滑动』可视区域的时候,你就需要手工重新计算并更新所有的子图层位置。
这个时候就需要CAScrollLayer
了。CAScrollLayer
有一个-scrollToPoint:
方法,它自动适应bounds
的原点以便图层内容出现在滑动的地方。注意,这就是它做的所有事情。前面提到过,Core Animation并不处理用户输入,所以CAScrollLayer
并不负责将触摸事件转换为滑动事件,既不渲染滚动条,也不实现任何iOS指定行为例如滑动反弹(当视图滑动超多了它的边界的将会反弹回正确的地方)。
让我们来用CAScrollLayer
来常见一个基本的UIScrollView
替代品。我们将会用CAScrollLayer
作为视图的宿主图层,并创建一个自定义的UIView
,然后用UIPanGestureRecognizer
实现触摸事件响应。这段代码见清单6.10. 图6.11是运行效果:ScrollView
显示了一个大于它的frame
的UIImageView
。
清单6.10 用CAScrollLayer
实现滑动视图
1 #import "ScrollView.h" 2 #import @implementation ScrollView 3 + (Class)layerClass 4 { 5 return [CAScrollLayer class]; 6 } 7 8 - (void)setUp 9 { 10 //enable clipping 11 self.layer.masksToBounds = YES; 12 13 //attach pan gesture recognizer 14 UIPanGestureRecognizer *recognizer = nil; 15 recognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(pan:)]; 16 [self addGestureRecognizer:recognizer]; 17 } 18 19 - (id)initWithFrame:(CGRect)frame 20 { 21 //this is called when view is created in code 22 if ((self = [super initWithFrame:frame])) { 23 [self setUp]; 24 } 25 return self; 26 } 27 28 - (void)awakeFromNib { 29 //this is called when view is created from a nib 30 [self setUp]; 31 } 32 33 - (void)pan:(UIPanGestureRecognizer *)recognizer 34 { 35 //get the offset by subtracting the pan gesture 36 //translation from the current bounds origin 37 CGPoint offset = self.bounds.origin; 38 offset.x -= [recognizer translationInView:self].x; 39 offset.y -= [recognizer translationInView:self].y; 40 41 //scroll the layer 42 [(CAScrollLayer *)self.layer scrollToPoint:offset]; 43 44 //reset the pan gesture translation 45 [recognizer setTranslation:CGPointZero inView:self]; 46 } 47 @end
图6.11 用UIScrollView
创建一个凑合的滑动视图
不同于UIScrollView
,我们定制的滑动视图类并没有实现任何形式的边界检查(bounds checking)。图层内容极有可能滑出视图的边界并无限滑下去。CAScrollLayer
并没有等同于UIScrollView
中contentSize
的属性,所以当CAScrollLayer
滑动的时候完全没有一个全局的可滑动区域的概念,也无法自适应它的边界原点至你指定的值。它之所以不能自适应边界大小是因为它不需要,内容完全可以超过边界。
那你一定会奇怪用CAScrollLayer
的意义到底何在,因为你可以简单地用一个普通的CALayer
然后手动适应边界原点啊。真相其实并不复杂,UIScrollView
并没有用CAScrollLayer
,事实上,就是简单的通过直接操作图层边界来实现滑动。
CAScrollLayer
有一个潜在的有用特性。如果你查看CAScrollLayer
的头文件,你就会注意到有一个扩展分类实现了一些方法和属性:
1 - (void)scrollPoint:(CGPoint)p; 2 - (void)scrollRectToVisible:(CGRect)r; 3 @property(readonly) CGRect visibleRect;
看到这些方法和属性名,你也许会以为这些方法给每个CALayer
实例增加了滑动功能。但是事实上他们只是放置在CAScrollLayer
中的图层的实用方法。scrollPoint:
方法从图层树中查找并找到第一个可用的CAScrollLayer
,然后滑动它使得指定点成为可视的。scrollRectToVisible:
方法实现了同样的事情只不过是作用在一个矩形上的。visibleRect
属性决定图层(如果存在的话)的哪部分是当前的可视区域。如果你自己实现这些方法就会相对容易明白一点,但是CAScrollLayer
帮你省了这些麻烦,所以当涉及到实现图层滑动的时候就可以用上了。