2012-06-25 18 views
2

我有一個實現徒手繪製的視圖,但我有一個小問題。我注意到在iPad 3上所有的東西都變成了地獄,所以我試圖更新我的繪圖代碼(可能就像我應該做的第一件事),只更新被撫摸的部分。然而,打開後的第一次衝程以及閒置約10秒後的第一次衝程非常緩慢。在所有事情都「熱身」之後,它就像黃油一樣平滑,每個drawRect只需要大約0.15ms。我不知道爲什麼,但是整個視圖矩形被標記爲第一個drawRect髒,第一個drawRect空閒後(然後大約需要150 ms更新)。堆棧跟蹤顯示我的矩形是由CABackingStoreUpdate_UIGraphicsGetCurrentContext()短生命週期

我試過繪製層如果矩形是巨大的,但後來我的整個背景一片空白(將重新出現,因爲我畫過舊區像樂透票)覆蓋。有沒有人有任何想法與UIGraphicsGetCurrentContext()?這是我能想象的唯一的麻煩。也就是說,我的觀點背景被上下文精靈拉下了,所以需要重新呈現自己。有什麼設置可以用來保持相同的上下文嗎?或者還有其他的東西在這裏......沒有必要在初始顯示之後更新完整的矩形。

我的drawRect很簡單:

- (void)drawRect:(CGRect)rect 
{ 
    CGContextRef c = mDrawingLayer ? CGLayerGetContext(mDrawingLayer) : NULL; 
    if(!mDrawingLayer) 
    { 
     c = UIGraphicsGetCurrentContext(); 
     mDrawingLayer = CGLayerCreateWithContext(c, self.bounds.size, NULL); 
     c = CGLayerGetContext(mDrawingLayer); 
     CGContextSetAllowsAntialiasing(c, true); 
     CGContextSetShouldAntialias(c, true); 
     CGContextSetLineCap(c, kCGLineCapRound); 
     CGContextSetLineJoin(c, kCGLineJoinRound); 
    } 

    if(mClearFlag) 
    { 
     CGContextClearRect(c, self.bounds); 
     mClearFlag = NO; 
    } 

    CGContextStrokePath(c); 
    CFAbsoluteTime startTime = CFAbsoluteTimeGetCurrent(); 
    CGContextDrawLayerInRect(UIGraphicsGetCurrentContext(), self.bounds, mDrawingLayer); 
    NSLog(@"%.2fms : %f x %f", (CFAbsoluteTimeGetCurrent() - startTime)*1000.f, rect.size.width, rect.size.height); 

} 
+0

您可能會嘗試重寫setNeedsDisplay和setNeedsDisplayInRect:因此您可以放置​​一個斷點並查看是否有某些事情意外使整個視圖無效。 –

+0

(另外,非常有趣的問題。) –

+0

@JesseRusak非常聰明的想法,但失效並不是調用的結果:(:(他們只在開始時被調用,從不再爲完整的矩形) – borrrden

回答

3

我發現了一個有用的線程上的Apple Dev Forums描述這個確切的問題。它只存在於iOS 5.0之後,理論上它是因爲Apple引入了一個雙緩衝系統,所以前兩個drawRects將始終是滿的。但是,沒有解釋爲什麼閒置後會再次發生這種情況。該理論認爲,底層緩衝區不受GPU的保證,並且會隨時丟棄,需要重新創建。該解決方案(直到蘋果發出某種真正的解決方案)是ping到緩衝區,這樣就不會被釋放:

mDisplayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(pingRect)]; 
[mDisplayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; 

- (void)pingRect 
{ 
    //Already drawing 
    if(mTouchCount > 0) return; 

    //Even touching just one pixel will keep the buffer alive 
    [self setNeedsDisplayInRect:CGRectMake(0, 0, 1, 1)]; 
} 

唯一的弱點是,如果用戶保持其手指完全靜止的超過5秒,但我認爲這是一個可以接受的風險。

編輯有趣的更新。事實證明,簡單地調用setNeedsDisplay就足以使緩衝區保持活動狀態,即使它立即返回。所以我說這對我的drawRect方法:

- (void)drawRect:(CGRect)rect 
{ 
    if(rect.size.width == 1.f) 
     return; 
    //... 
} 

希望,這將抑制用電量,這刷新方法必然會增加。

+0

您可能想調用因爲功率使用原因,頻率更低 –

+1

@JesseRusak我關心同樣的事情,但它不工作,除非它是60赫茲(儘管這隻會導致一次調用完全drawRect,所以我假設沒有60赫茲它錯過了雙緩衝系統中的緩衝區) – borrrden

+0

Huh。哇,這真是太瘋狂了 –