2

フリーハンド描画を実装したビューがありますが、小さな問題があります。iPad 3ですべてが地獄に落ちたことに気づいたので、ストロークされた部分だけを更新するように描画コードを更新しようとしました(おそらく最初に行うべきだったように)。ただし、開封後の最初のストロークと約10秒のアイドル後の最初のストロークは非常に遅いです。すべてが「ウォームアップ」された後、それはバターのように滑らかで、drawRectあたり約0.15msしかかかりません。理由はわかりませんが、最初のdrawRectとアイドル後の最初のdrawRectで、ビューの長方形全体がダーティとしてマークされています(更新には約150ミリ秒かかります)。スタックトレースは、私の長方形がオーバーライドされていることを示していますCABackingStoreUpdate_

長方形が大きい場合はレイヤーを描画しないようにしましたが、コンテキスト全体が空白になります(宝くじのように古い領域を描画すると再び表示されます)。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);

}
4

1 に答える 1

3

Apple Dev Forumsで、この正確な問題を説明する便利なスレッドを見つけました。これはiOS5.0以降にのみ存在し、Appleがダブルバッファリングシステムを導入したためであるという理論があり、最初の2つのdrawRectは常にいっぱいになります。ただし、アイドル状態の後にこれが再び発生する理由についての説明はありません。理論では、基礎となるバッファーはGPUによって保証されておらず、これは気まぐれで破棄され、再作成する必要があります。解決策(Appleが何らかの実際の解決策を発行するまで)は、バッファが解放されないように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;
    //...
}

うまくいけば、このリフレッシュ方法が確実に増加する電力使用量を抑えることができます。

于 2012-06-26T01:26:52.350 に答える