# 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`實現滑動視圖
~~~
#import "ScrollView.h"
#import @implementation ScrollView
+ (Class)layerClass
{
return [CAScrollLayer class];
}
- (void)setUp
{
//enable clipping
self.layer.masksToBounds = YES;
//attach pan gesture recognizer
UIPanGestureRecognizer *recognizer = nil;
recognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(pan:)];
[self addGestureRecognizer:recognizer];
}
- (id)initWithFrame:(CGRect)frame
{
//this is called when view is created in code
if ((self = [super initWithFrame:frame])) {
[self setUp];
}
return self;
}
- (void)awakeFromNib {
//this is called when view is created from a nib
[self setUp];
}
- (void)pan:(UIPanGestureRecognizer *)recognizer
{
//get the offset by subtracting the pan gesture
//translation from the current bounds origin
CGPoint offset = self.bounds.origin;
offset.x -= [recognizer translationInView:self].x;
offset.y -= [recognizer translationInView:self].y;
//scroll the layer
[(CAScrollLayer *)self.layer scrollToPoint:offset];
//reset the pan gesture translation
[recognizer setTranslation:CGPointZero inView:self];
}
@end
~~~
圖6.11 用`UIScrollView`創建一個湊合的滑動視圖
不同于`UIScrollView`,我們定制的滑動視圖類并沒有實現任何形式的邊界檢查(bounds checking)。圖層內容極有可能滑出視圖的邊界并無限滑下去。`CAScrollLayer`并沒有等同于`UIScrollView`中`contentSize`的屬性,所以當`CAScrollLayer`滑動的時候完全沒有一個全局的可滑動區域的概念,也無法自適應它的邊界原點至你指定的值。它之所以不能自適應邊界大小是因為它不需要,內容完全可以超過邊界。
那你一定會奇怪用`CAScrollLayer`的意義到底何在,因為你可以簡單地用一個普通的`CALayer`然后手動適應邊界原點啊。真相其實并不復雜,`UIScrollView`并沒有用`CAScrollLayer`,事實上,就是簡單的通過直接操作圖層邊界來實現滑動。
`CAScrollLayer`有一個潛在的有用特性。如果你查看`CAScrollLayer`的頭文件,你就會注意到有一個擴展分類實現了一些方法和屬性:
~~~
- (void)scrollPoint:(CGPoint)p;
- (void)scrollRectToVisible:(CGRect)r;
@property(readonly) CGRect visibleRect;
~~~
看到這些方法和屬性名,你也許會以為這些方法給每個`CALayer`實例增加了滑動功能。但是事實上他們只是放置在`CAScrollLayer`中的圖層的實用方法。`scrollPoint:`方法從圖層樹中查找并找到第一個可用的`CAScrollLayer`,然后滑動它使得指定點成為可視的。`scrollRectToVisible:`方法實現了同樣的事情只不過是作用在一個矩形上的。`visibleRect`屬性決定圖層(如果存在的話)的哪部分是當前的可視區域。如果你自己實現這些方法就會相對容易明白一點,但是`CAScrollLayer`幫你省了這些麻煩,所以當涉及到實現圖層滑動的時候就可以用上了。
- Introduction
- 1. 圖層樹
- 1.1 圖層與視圖
- 1.2 圖層的能力
- 1.3 使用圖層
- 1.4 總結
- 2. 寄宿圖
- 2.1 contents屬性
- 2.2 Custom Drawing
- 2.3 總結
- 3. 圖層幾何學
- 3.1 布局
- 3.2 錨點
- 3.3 坐標系
- 3.4 Hit Testing
- 3.5 自動布局
- 3.6 總結
- 4. 視覺效果
- 4.1 圓角
- 4.2 圖層邊框
- 4.3 陰影
- 4.4 圖層蒙板
- 4.5 拉伸過濾
- 4.6 組透明
- 4.7 總結
- 5. 變換
- 5.1 仿射變換
- 5.2 3D變換
- 5.3 固體對象
- 5.4 總結
- 6. 專用圖層
- 6.1 CAShapeLayer
- 6.2 CATextLayer
- 6.3 CATransformLayer
- 6.4 CAGradientLayer
- 6.5 CAReplicatorLayer
- 6.6 CAScrollLayer
- 6.7 CATiledLayer
- 6.8 CAEmitterLayer
- 6.9 CAEAGLLayer
- 6.10 AVPlayerLayer
- 6.11 總結
- 7. 隱式動畫
- 7.1 事務
- 7.2 完成塊
- 7.3 圖層行為
- 7.4 呈現與模型
- 7.5 總結
- 8. 顯式動畫
- 8.1 屬性動畫
- 8.2 動畫組
- 8.3 過渡
- 8.4 在動畫過程中取消動畫
- 8.5 總結
- 9. 圖層時間
- 9.1 CAMediaTiming協議
- 9.2 層級關系時間
- 9.3 手動動畫
- 9.4 總結
- 10. 緩沖
- 10.1 動畫速度
- 10.2 自定義緩沖函數
- 10.3 總結
- 11. 基于定時器的動畫
- 11.1 定時幀
- 11.2 物理模擬
- 12. 性能調優
- 12.1. CPU VS GPU
- 12.2 測量,而不是猜測
- 12.3 Instruments
- 12.4 總結
- 13. 高效繪圖
- 13.1 軟件繪圖
- 13.2 矢量圖形
- 13.3 臟矩形
- 13.4 異步繪制
- 13.5 總結
- 14. 圖像IO
- 14.1 加載和潛伏
- 14.2 緩存
- 14.3 文件格式
- 14.4 總結
- 15. 圖層性能
- 15.1 隱式繪制
- 15.2 離屏渲染
- 15.3 混合和過度繪制
- 15.4 減少圖層數量
- 15.5 總結