2012-05-28 58 views
3

我不是很瞭解CALayer的displaydrawInContext如何與視圖中的drawRect相關。在iOS上,setNeedsDisplay確實不會導致drawRect被調用......除非CALayer的display或drawInContext最終調用drawRect?

如果我有一個NSTimer每隔1秒設置[self.view setNeedsDisplay],則每1秒調用一次drawRect,如drawRect內部的NSLog語句所示。

但是,如果我繼承CALayer並將其用於視圖,如果我使display方法爲空,則現在不會調用drawRect更新:但是每隔1秒調用一次display,如NSLog語句所示。

如果我刪除空的display方法並添加一個空的drawInContext方法,再次,drawRect永遠不會被調用。 更新:但是每隔1秒調用一次drawInContext,如NSLog語句所示。

究竟發生了什麼?看起來display可以有選擇地撥打drawInContextdrawInContext可以有選擇地以某種方式撥打drawRect(怎麼辦?),但這裏真實的情況是什麼?


更新:有更多的線索答案:

我改變了CoolLayer.m代碼如下:

-(void) display { 
    NSLog(@"In CoolLayer's display method"); 
    [super display]; 
} 

-(void) drawInContext:(CGContextRef)ctx { 
    NSLog(@"In CoolLayer's drawInContext method"); 
    [super drawInContext:ctx]; 
} 

所以,我們說,如果有一個月亮(作爲Core Graphics繪製的一個圓圈)在視圖中的位置(100,100)處,現在我將其更改爲位置(200,200),自然,我將調用[self.view setNeedsDisplay],現在,CALayer將根本沒有緩存用於新視圖圖像,就像我的drawRect指示現在應如何顯示月亮。

即使如此,切入點是CALayer的的display,然後CALayer的的drawInContext:如果我在drawRect設置一個斷點,調用堆棧顯示:

enter image description here

所以我們可以看到,CoolLayer的display是先輸入CALayer的display,然後輸入CoolLayer的drawInContext,然後輸入CALayer的drawInContext,儘管在這種情況下,新圖像不存在這樣的緩存。

然後最後,CALayer的drawInContext調用代表的drawLayer:InContext。該委託是視圖(FooView或UIView)...和drawLayer:InContext是UIView中的默認實現(因爲我沒有覆蓋它)。終於,drawLayer:InContext調用drawRect

所以我猜兩點:爲什麼它進入CALayer即使沒有緩存的圖像?因爲通過這種機制,圖像在上下文中繪製,並最終返回到display,並且CGImage是從此上下文創建的,然後現在將其設置爲緩存的新圖像。這就是CALayer緩存圖像的方式。

我不太確定的另一件事是:如果[self.view setNeedsDisplay]總是觸發drawRect被調用,那麼何時可以使用CALayer中的緩存圖像?可能是...在Mac OS X上,當另一個窗口覆蓋一個窗口時,現在頂部窗口被移開。現在我們不需要調用drawRect來重繪所有內容,但可以使用CALayer中的緩存圖像。或者在iOS上,如果我們停止應用程序,執行其他操作,然後返回應用程序,則可以使用緩存圖像,而不是調用drawRect。但如何區分這兩種「髒」?一個是「未知的骯髒」 - 月亮需要重新繪製,如邏輯(它也可以使用隨機數字作爲座標)。其他類型的骯髒是它被掩蓋或使其消失,現在需要重新顯示。

回答

1

的UIView的更新程序是基於dirty狀態意認爲,如果沒有改變它的外觀是不可能被重新繪製。

這是開發人員參考中提到的內部實現。

+0

CALayer如何發揮作用? –

+0

在文檔中明確指出,'setNeedsDisplay'只適用於UIKit和Core Graphics繪圖,我相信髒視圖更新對它們都是有效的。如文檔所述,請注意這對CAEAGLLayer無效。 –

+0

問題是如果我們使用'setNeedsDisplay',但是覆蓋CALayer的方法,那麼'drawRect'不會被調用,爲什麼? –

12

當一個圖層需要顯示並且沒有有效的後備存儲時(可能是因爲圖層收到了一條setNeedsDisplay消息),系統會發送display消息到圖層。

-[CALayer display]方法看起來大致是這樣的:

- (void)display { 
    if ([self.delegate respondsToSelector:@selector(displayLayer:)]) { 
     [[self.delegate retain] displayLayer:self]; 
     [self.delegate release]; 
     return; 
    } 

    CABackingStoreRef backing = _backingStore; 
    if (!backing) { 
     backing = _backingStore = ... code here to create and configure 
      the CABackingStore properly, given the layer size, isOpaque, 
      contentScale, etc. 
    } 

    CGContextRef gc = ... code here to create a CGContext that draws into backing, 
     with the proper clip region 
    ... also code to set up a bitmap in memory shared with the WindowServer process 

    [self drawInContext:gc]; 
    self.contents = backing; 
} 

所以,如果你重寫display,沒有出現這種情況,除非你叫[super display]。如果你在FooView實現displayLayer:,你必須創建自己的CGImage不知何故,並將其存儲在該層的contents財產。

-[CALayer drawInContext:]方法看起來大致是這樣的:

- (void)drawInContext:(CGContextRef)gc { 
    if ([self.delegate respondsToSelector:@selector(drawLayer:inContext:)]) { 
     [[self.delegate retain] drawLayer:self inContext:gc]; 
     [self.delegate release]; 
     return; 
    } else { 
     CAAction *action = [self actionForKey:@"onDraw"]; 
     if (action) { 
      NSDictionary *args = [NSDictionary dictionaryWithObject:gc forKey:@"context"]; 
      [action runActionForKey:@"onDraw" object:self arguments:args]; 
     } 
    } 
} 

onDraw動作不據我所知記錄。

-[UIView drawLayer:inContext:]方法看起來大致是這樣的:

- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)gc { 
    set gc's stroke and fill color spaces to device RGB; 
    UIGraphicsPushContext(gc); 
    fill gc with the view's background color; 
    if ([self respondsToSelector:@selector(drawRect:)]) { 
     [self drawRect:CGContextGetClipBoundingBox(gc)]; 
    } 
    UIGraphicsPopContext(gc); 
} 
+0

+1,你怎麼知道?你讀過源代碼還是隻是猜測? –

+4

我使用Hopper來反彙編IOS模擬器庫。 –

0

實現一個drawInContext或顯示器或drawRect中告訴操作系統你想叫哪一個時,認爲是髒(needsDisplay)。選擇一個你想調用髒視圖並實現的視圖,並且不要把你依賴的代碼放在其他視圖中執行。

相關問題