W3Cschool
恭喜您成為首批注冊(cè)用戶
獲得88經(jīng)驗(yàn)值獎(jiǎng)勵(lì)
對(duì)于一個(gè)未轉(zhuǎn)換的圖層,它的bounds
和它的frame
是一樣的,frame
屬性是由bounds
屬性自動(dòng)計(jì)算而出的,所以更改任意一個(gè)值都會(huì)更新其他值。
但是如果你只想顯示一個(gè)大圖層里面的一小部分呢。比如說,你可能有一個(gè)很大的圖片,你希望用戶能夠隨意滑動(dòng),或者是一個(gè)數(shù)據(jù)或文本的長列表。在一個(gè)典型的iOS應(yīng)用中,你可能會(huì)用到UITableView
或是UIScrollView
,但是對(duì)于獨(dú)立的圖層來說,什么會(huì)等價(jià)于剛剛提到的UITableView
和UIScrollView
呢?
在第二章中,我們探索了圖層的contentsRect
屬性的用法,它的確是能夠解決在圖層中小地方顯示大圖片的解決方法。但是如果你的圖層包含子圖層那它就不是一個(gè)非常好的解決方案,因?yàn)?,這樣做的話每次你想『滑動(dòng)』可視區(qū)域的時(shí)候,你就需要手工重新計(jì)算并更新所有的子圖層位置。
這個(gè)時(shí)候就需要CAScrollLayer
了。CAScrollLayer
有一個(gè)-scrollToPoint:
方法,它自動(dòng)適應(yīng)bounds
的原點(diǎn)以便圖層內(nèi)容出現(xiàn)在滑動(dòng)的地方。注意,這就是它做的所有事情。前面提到過,Core Animation并不處理用戶輸入,所以CAScrollLayer
并不負(fù)責(zé)將觸摸事件轉(zhuǎn)換為滑動(dòng)事件,既不渲染滾動(dòng)條,也不實(shí)現(xiàn)任何iOS指定行為例如滑動(dòng)反彈(當(dāng)視圖滑動(dòng)超多了它的邊界的將會(huì)反彈回正確的地方)。
讓我們來用CAScrollLayer
來常見一個(gè)基本的UIScrollView
替代品。我們將會(huì)用CAScrollLayer
作為視圖的宿主圖層,并創(chuàng)建一個(gè)自定義的UIView
,然后用UIPanGestureRecognizer
實(shí)現(xiàn)觸摸事件響應(yīng)。這段代碼見清單6.10. 圖6.11是運(yùn)行效果:ScrollView
顯示了一個(gè)大于它的frame
的UIImageView
。
清單6.10 用CAScrollLayer
實(shí)現(xiàn)滑動(dòng)視圖
#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
創(chuàng)建一個(gè)湊合的滑動(dòng)視圖
不同于UIScrollView
,我們定制的滑動(dòng)視圖類并沒有實(shí)現(xiàn)任何形式的邊界檢查(bounds checking)。圖層內(nèi)容極有可能滑出視圖的邊界并無限滑下去。CAScrollLayer
并沒有等同于UIScrollView
中contentSize
的屬性,所以當(dāng)CAScrollLayer
滑動(dòng)的時(shí)候完全沒有一個(gè)全局的可滑動(dòng)區(qū)域的概念,也無法自適應(yīng)它的邊界原點(diǎn)至你指定的值。它之所以不能自適應(yīng)邊界大小是因?yàn)樗恍枰?,?nèi)容完全可以超過邊界。
那你一定會(huì)奇怪用CAScrollLayer
的意義到底何在,因?yàn)槟憧梢院唵蔚赜靡粋€(gè)普通的CALayer
然后手動(dòng)適應(yīng)邊界原點(diǎn)啊。真相其實(shí)并不復(fù)雜,UIScrollView
并沒有用CAScrollLayer
,事實(shí)上,就是簡單的通過直接操作圖層邊界來實(shí)現(xiàn)滑動(dòng)。
CAScrollLayer
有一個(gè)潛在的有用特性。如果你查看CAScrollLayer
的頭文件,你就會(huì)注意到有一個(gè)擴(kuò)展分類實(shí)現(xiàn)了一些方法和屬性:
- (void)scrollPoint:(CGPoint)p;
- (void)scrollRectToVisible:(CGRect)r;
@property(readonly) CGRect visibleRect;
看到這些方法和屬性名,你也許會(huì)以為這些方法給每個(gè)CALayer
實(shí)例增加了滑動(dòng)功能。但是事實(shí)上他們只是放置在CAScrollLayer
中的圖層的實(shí)用方法。scrollPoint:
方法從圖層樹中查找并找到第一個(gè)可用的CAScrollLayer
,然后滑動(dòng)它使得指定點(diǎn)成為可視的。scrollRectToVisible:
方法實(shí)現(xiàn)了同樣的事情只不過是作用在一個(gè)矩形上的。visibleRect
屬性決定圖層(如果存在的話)的哪部分是當(dāng)前的可視區(qū)域。如果你自己實(shí)現(xiàn)這些方法就會(huì)相對(duì)容易明白一點(diǎn),但是CAScrollLayer
幫你省了這些麻煩,所以當(dāng)涉及到實(shí)現(xiàn)圖層滑動(dòng)的時(shí)候就可以用上了。
Copyright©2021 w3cschool編程獅|閩ICP備15016281號(hào)-3|閩公網(wǎng)安備35020302033924號(hào)
違法和不良信息舉報(bào)電話:173-0602-2364|舉報(bào)郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號(hào)
聯(lián)系方式:
更多建議: