3

displayCALayerがどのように表示され、どのようにdrawInContext関連しているかがよくわかりませんdrawRect

[self.view setNeedsDisplay]1秒ごとに設定するNSTimerがある場合、。drawRect内のNSLogステートメントで示されているように、1秒ごとに呼び出されdrawRectます。

しかし、CALayerをサブクラス化し、それをビューに使用する場合、displayメソッドを空にすると、nowdrawRectは呼び出されません。 更新:ただしdisplay、NSLogステートメントで示されているように、1秒ごとに呼び出されます。

その空のメソッドを削除して空のdisplayメソッドを追加するとdrawInContext、再びdrawRect呼び出されることはありません。 更新:ただしdrawInContext、NSLogステートメントで示されているように、1秒ごとに呼び出されます。

正確には何が起こっているのですか?選択的に呼び出すことができ、何とかしてdisplay選択的に呼び出すことができるようです(どのように?)が、ここでの実際の状況は何ですか?drawInContextdrawInContextdrawRect


更新:答えにはもっと手がかりがあります:

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];
}

たとえば、ビューの場所(100,100)に月(Core Graphicsによって描画された円として)があり、それを場所(200,200)に変更した場合、当然、と呼びます[self.view setNeedsDisplay]。これで、CALayerはdrawRect月がどのように表示されるかを指示するので、新しいビュー画像のキャッシュはまったくありません。

それでも、エントリポイントはCALayerdisplayであり、次にCALayerdrawInContextです。ブレークポイントをに設定するdrawRectと、コールスタックは次のように表示されます。

ここに画像の説明を入力してください

したがって、CoolLayerdisplayが最初に入力され、次にCALayer、display次にCoolLayer、次にCALayerに移動することがわかります。この状況では、新しいイメージ用のそのようなキャッシュは存在しません。drawInContextdrawInContext

そして最後に、CALayerdrawInContextはデリゲートを呼び出しますdrawLayer:InContext。デリゲートはビュー(FooViewまたはUIView)...でありdrawLayer:InContext、UIViewのデフォルトの実装です(オーバーライドしなかったため)。最後にそれをdrawLayer:InContext呼び出しますdrawRect

だから私は2つのポイントを推測しています:画像のキャッシュがないのになぜそれがCALayerに入るのですか?このメカニズムにより、画像はコンテキストで描画され、最終的にに戻りdisplay、このコンテキストからCGImageが作成され、キャッシュされた新しい画像として設定されます。これは、CALayerが画像をキャッシュする方法です。

もう1つよくわからないのは、[self.view setNeedsDisplay]常に呼び出されるようにトリガーdrawRectする場合、CALayerにキャッシュされた画像をいつ使用できるかということです。それは...MacOS Xで、別のウィンドウがウィンドウを覆い、上部のウィンドウが移動したときです。これで、すべてを再描画するために呼び出す必要はありませんがdrawRect、CALayerでキャッシュされた画像を使用できます。または、iOSでは、アプリを停止し、何か他のことをしてアプリに戻ると、を呼び出す代わりに、キャッシュされた画像を使用できますdrawRect。しかし、これら2つのタイプの「ダーティ」をどのように区別するのでしょうか。1つは「不明なダーティ」です。これは、ロジックの指示に従って月を再描画する必要があることdrawRectです(座標にも乱数を使用できます)。他のタイプの汚れは、それが覆われているか、消えるようにされており、今度は再表示する必要があることです。

4

3 に答える 3

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]。また、で実装displayLayer:する場合FooViewは、独自の方法で作成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);
}
于 2012-05-29T20:06:21.760 に答える
1

UIView の更新手順は状態に基づいており、dirty外観に変更がない場合、ビューが再描画される可能性は低いことを意味します。

これは、開発者リファレンスで言及されている内部実装です。

于 2012-05-28T22:30:11.883 に答える
0

drawInContext、display、または drawRect を実装すると、ビューがダーティな場合 (needsDisplay) にどれを呼び出すかが OS に通知されます。ダーティビューに対して呼び出したいものを選択して実装し、実行に依存するコードを他のビューに配置しないでください。

于 2012-05-30T15:49:14.280 に答える