web-dev-qa-db-ja.com

CALayerのcontentsScaleプロパティを設定する必要があるのはいつですか?

ドキュメント Appleは、次の場合にレイヤーのスケール係数を考慮する必要があると述べています。

  • さまざまな倍率で追加のCoreAnimationレイヤーを作成し、それらを独自のコンテンツに合成します
  • CoreAnimationレイヤーのcontentsプロパティを直接設定します

最後の声明は私には完全に明確ではありません。彼らはまた言う:

Core Animationレイヤーを直接使用してコンテンツを提供するアプリケーションでは、倍率を考慮して描画コードを調整する必要がある場合があります。通常、ビューのdrawRect:メソッドまたはレイヤーのデリゲートのdrawLayer:inContext:メソッドで描画すると、システムは自動的にグラフィックスコンテキストを調整して、倍率を考慮します。

通常または常に?これは、ビューのホストされたバッキングレイヤー、またはバッキングレイヤーにサブレイヤーとして追加したすべてのレイヤーにのみ有効ですか?彼らはまた言うので:

アプリケーションがビューが関連付けられていないレイヤーを作成する場合、新しいレイヤーオブジェクトのスケール係数は最初は1.0に設定されます。その倍率を変更せず、その後高解像度画面にレイヤーを描画すると、倍率の違いを補正するために、レイヤーのコンテンツが自動的にスケーリングされます。コンテンツを拡大縮小したくない場合は、レイヤーの倍率を2.0に変更できますが、高解像度のコンテンツを提供せずに拡大縮小すると、既存のコンテンツが予想よりも小さく表示される場合があります。この問題を解決するには、レイヤーに高解像度のコンテンツを提供する必要があります。


ビューのないレイヤーを想像するのに苦労しています。レイヤーの階層を想像できますが、それらを表示したい場合は、ビューのバッキングレイヤーのサブ階層として追加する必要があります。それらを使用する他の手段(コンテキストでの作成やレンダリングなど)?

Core Animationの合成エンジンは、各レイヤーのcontentsScaleプロパティを調べて、合成中にそのレイヤーのコンテンツをスケーリングする必要があるかどうかを判断します。アプリケーションがビューが関連付けられていないレイヤーを作成する場合、新しいレイヤーオブジェクトのスケール係数は最初は1.0に設定されます。その倍率を変更せず、その後高解像度画面にレイヤーを描画すると、倍率の違いを補正するために、レイヤーのコンテンツが自動的にスケーリングされます。コンテンツを拡大縮小したくない場合は、レイヤーの倍率を2.0に変更できますが、高解像度のコンテンツを提供せずに拡大縮小すると、既存のコンテンツが予想よりも小さく表示される場合があります。この問題を解決するには、レイヤーに高解像度のコンテンツを提供する必要があります。

それは実際にはどういう意味ですか? layer.contents = (id)image.CGImageのようなことをするときは、contentsScaleを設定する必要があります。
レイヤーがデリゲートメソッドを呼び出してコンテンツを描画するときにも設定する必要がありますかdrawLayer:inContext:そしてレイヤーはビューのバッキングレイヤーではありませんか?

31
Andrea

re:-drawRect:または-drawLayer:inContextを介したレイヤーコンテンツ:

通常または常に?これは、ビューのホストされたバッキングレイヤー、またはバッキングレイヤーにサブレイヤーとして追加したすべてのレイヤーにのみ有効ですか?

常に。これらのメソッドが呼び出されると、現在のコンテキストにはすでにスケーリング係数が適用されています。たとえば、レイヤーが10pt x 10ptの場合、contentsScaleに関係なく、境界は{0,0,10,10}になります。 contentScale = 2の場合(たとえば、網膜上にいる場合)、これは実際には20x20ピクセルです。 CALayerは、すでに20x20pxのバッキングストアをセットアップし、スケール係数CGContextScaleCTM(context、2、2)を適用しています。

これは、どちらの方法でも10x10の概念空間で描画できるようにするためですが、ピクセルが2倍になると、バッキングストアも2倍になります。

ビューのないレイヤーを想像するのに苦労しています。レイヤーの階層を想像できますが、それらを表示したい場合は、ビューのバッキングレイヤーのサブ階層として追加するか、他の方法でレイヤーを使用する必要があります。 (コンテキストでの作成とレンダリングのように)?

それらを作成してサブレイヤーとして追加し、デリゲートを-drawLayer:inContext:を実装するオブジェクトに設定するか、レイヤーのコンテンツを直接設定することができます。 iOSでは、UIViewはCALayerのかなり浅いラッパーであるため、サブレイヤーを直接作成するのではなく、サブビューを作成するのが一般的に理にかなっています。そのため、この使用法にあまり慣れていません。 OS Xでは、レイヤーに裏打ちされたNSViewはCALayerの大きくて複雑なラッパーであるため、単一のビュー内でレイヤーの階層全体を作成する方がはるかに理にかなっています。

それは実際にはどういう意味ですか?そのlayer.contents =(id)image.CGImageのようなことをするとき、contentsScaleを設定する必要があります。

はい。しかし、ドキュメントのこの箇所が示しているのは、見苦しくなりたくない場合は、contentsScaleが2の10pt x 10ptレイヤーには、20px x20pxの画像のコンテンツが必要であるということです。つまりアーティファクトのスケーリングを回避したい場合は、レイヤーのコンテンツを直接設定するときに、レイヤーのcontentsScaleに注意する必要があります。

レイヤーがデリゲートメソッドdrawLayer:inContext:を呼び出してコンテンツを描画し、そのレイヤーがビューのバッキングレイヤーではない場合にも、これを設定する必要がありますか?

はい。 (UIViewを介して作成されていない)自分でレイヤーを作成している場合は、手動でスケールを設定する必要があります。通常、これは単なるlayer.contentsScale = [[UIScreen mainScreen] scale]です。 (繰り返しますが、OS Xでは、実行中に画面が出入りする可能性があるため、これは少し複雑です。)

16
gregomni

レイヤーはほとんどの場合ビュー内で使用されますが、レイヤーはアドホックに作成され、ビューのレイヤーのサブレイヤーとして追加されたか、別のレイヤーのマスクとして割り当てられている可能性があります。どちらの場合も、contentsScaleプロパティを設定する必要があります。例えば:

UIView *myMaskedView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
CALayer *myMaskLayer = [CALayer new];
myMaskLayer.frame = myMaskedView.bounds;
UIImage *maskImage = [UIImage imageNamed:@"some_image"];
myMaskLayer.contents = (id)maskImage.CGImage;
myMaskLayer.contentsScale = maskImage.scale;
myMaskedView.layer.mask = myMaskLayer;

各レイヤーを使用してビューをバックアップせずに、アニメーションを実行するためのレイヤーの階層を構築することもできます。

UIView *myView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];

CALayer *sublayerA = [CALayer new];
sublayerA.contentsScale = myView.layer.contentsScale;
[myView.layer addSublayer:sublayerA];

CALayer *sublayerB = [CALayer new];
sublayerB.contentsScale = myView.layer.contentsScale;
[myView.layer addSublayer:sublayerB];

// Continue adding layers   

これは、ネイティブ画面スケールが2.0のRetinaデバイスにとって特に重要です。デフォルトのスケールが1.0のレイヤーは、目立つピクセル化を表示します。

6
Austin

OS Xでは、基本的に

  1. ウィンドウの-backingScaleFactorをつかみます
  2. 各(!)レイヤーの-contentsScaleをそのscaleFactorに設定します
  3. コントローラに-layer:shouldInheritContentsScale:fromWindow:を実装する
  4. 各レイヤーの-delegateをそのコントローラーに設定します

SOは、外部URLを参照するだけですが、これを説明するのは難しいです。スケールファクターに関して正しく機能するコードを次に示します。

https://github.com/JanX2/ScrollingAboutWindow

お役に立てば幸いです。

5
JanX2