2011-04-09 83 views
0

我已經重寫了的UIView做一些自定義繪製:iPhone - 優化的drawRect

// ======================================================================================== 
- (void) drawRect:(CGRect)rect { 

    float alphaStep = (kMaxAlpha - kMinAlpha)/(self.nbLines - 1); 
    float alphaValue = kMaxAlpha; 

    float lineToDrawY = self.headLineYPos; 
    float lineWidth = kScanLineMaxWidth; 
    float lineWidthStep = (kScanLineMaxWidth - kScanLineMinWidth)/(self.nbLines - 1); 

    CGContextRef context = UIGraphicsGetCurrentContext(); 
    for (int i=1; i<=self.nbLines; i++) { 

     CGContextBeginPath(context); 
     CGContextSetLineWidth(context, lineWidth); 
     CGContextSetStrokeColorWithColor(context, [self.scanColor colorWithAlphaComponent:alphaValue].CGColor); 

     CGContextMoveToPoint(context, 0, lineToDrawY); 
     CGContextAddLineToPoint(context, [self bounds].size.width, lineToDrawY); 
     CGContextStrokePath(context); 

     lineToDrawY -= kOneLineJumpDistance; 

     lineWidth = MAX(0.5, lineWidth - lineWidthStep); 
     alphaValue = MAX(0, alphaValue - alphaStep); 
    } 
} 

self.nbLines可能是30-40左右,可能是100

但繪製的這部分非常耗時,並且我無法每2或3毫秒進行屏幕更新。這需要將屏幕動畫的總時間保持在一秒以內(我每隔xxx秒將頭部線移動xxx像素,但增加太多會導致閃爍)。

我該如何優化?

+0

你期待有多少'nbLines'? – FreeAsInBeer 2011-04-09 13:11:44

+0

@FreeAsInBeer:大約30-40,可能是100 – Oliver 2011-04-09 13:19:03

+0

它需要花費多少毫秒?更新每2或3毫秒將是一個難以達到的目標。這是333 - 500 fps,我很肯定iPhone不能(也不想)擊中。 – FreeAsInBeer 2011-04-09 13:24:18

回答

3

如果您實際上正在改變/動畫每一個線段,而不是僅僅添加一個段到最後,那麼切換到使用OpenGL ES進行繪圖可能是允許足夠高的動畫幀速率的唯一選項。在iOS設備上,Open GL將使用GPU硬件呈現多邊形(線條),而不是像CG繪圖那樣在軟件中描線。

如果只添加或更改接近尾聲的部分,然後合成方法可以工作(使用多個CALayers),在那裏你不要再渲染了一大羣不改變每一幀段。

此外,iOS設備上的最大幀速率爲60 Hz,因此您無需每隔16.6 mS更頻繁地渲染一次。每2或3 mS重新渲染將對顯示屏不可見。

+0

是否意味着我將無法在320 x 16.6 = 5312 ms的320像素距離內繪製一條線,最多可減少1像素乘1像素?這是不正確的,我在1.5秒內做到了這一點,沒有「跳躍」,如果沒有顯示一步就會發生。 – Oliver 2011-04-09 23:35:45

+1

@Oliver - 物理LCD顯示屏實際上不會每60秒更新一次。所以這是可能的最小時間間隔,您可以看到任何可見的「跳躍」。與電影膠片相比,這種速度相當快,僅以24幀/秒的速度改變畫面,仍然提供運動幻覺。 – hotpaw2 2011-04-10 04:56:07

1

不幸的是,drawRect不是做動畫的最佳地方。我認爲原因是因爲drawRect的結果被複制到一個新紋理(CALayer)中,然後顯示在屏幕上。

如果你想要做的動畫不能與UIView的動畫或CoreAnimation東西,你可以做來完成的:

  1. 確保您不會在不必要的經常調用needsDisplay。一旦你畫了一次,結果被緩存在一個CALayer中,所以drawRect應該只需要在某些東西改變時被調用。

  2. 由於您正在製作一個更新很多的視圖,請嘗試使用needsDisplayInRect而不是needsDisplay。然後重構你的代碼,只更新在drawRect中傳遞的部分。

  3. 如果你還沒有得到你所需要的性能,你應該看看圖紙到CGLayer直接或使用庫像

+0

我認爲你的意思是CALayer,而不是CGLayer。 CGLayers在Core Graphics中用於繪圖元素。 CALayers是支持UIViews的核心動畫層,以及UIViews渲染的內容。 – 2011-04-09 16:45:54

+0

你的權利。我已經更新了我的答案。 – 2011-04-10 04:58:08

+0

@JoeBlow我應該說「drawRect不是做動畫的最佳地方。」如果您使用NSTimers將屬性放置到needsDisplay中,則可以在drawRect中設置動畫,但使用可動畫的UIView屬性或CoreAnimation製作平滑的動畫通常要容易得多。更不用說後者更有效率。 – 2011-04-14 03:59:47

1

我認爲,如果你使用CoreAnimation的可能性可以將你的圖片分成只能相互移動的圖層。 AFAIK的繪圖只出現一次,並由GPU完成。

也許你可以畫出頭部線條,應該在單獨的圖層上進行動畫,然後使用CoreAnimation爲該圖層設置動畫效果。

我不確定這是否有幫助,因爲我無法想象你從繪圖代碼單獨繪製。