6.6 CAScrollLayer 圖層

2021-09-14 16:22 更新

CAScrollLayer

對(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à)于剛剛提到的UITableViewUIScrollView呢?

在第二章中,我們探索了圖層的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è)大于它的frameUIImageView。

清單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并沒有等同于UIScrollViewcontentSize的屬性,所以當(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í)候就可以用上了。

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)