contentScale 属性は、ビュー サイズに対するホスト イメージのピクセル サイズの比率を定義します。デフォルトでは、値が 1.0 の浮動小数点数です。
contentScale の目的はそれほど明白ではありません。必ずしも画面上の画像に影響を与えるわけではありません。この例で別の値を設定してみると、まったく効果がないことがわかります。これは、contentsGravity プロパティが設定されているため、コンテンツがレイヤーの境界に合わせて拡大されたためです。
レイヤーのコンテンツ画像を単に拡大したい場合は、レイヤーのtransformプロパティとaffineTransformプロパティを使用してこの目的を達成できます(これについては、第5章「変換」を参照してください)。これ(ズームインを参照)
contentScale 属性は、実際には高解像度 (Hi-DPI または Retina とも呼ばれる) 画面をサポートするメカニズムの一部です。これは、レイヤーを描画するときにホスト イメージ用に作成されるスペースのサイズと、表示する必要があるイメージの範囲を決定するために使用されます (contentsGravity 属性が設定されていないと仮定します)。 UIView には、同様の、ほとんど使用されない contentScaleFactor プロパティがあります。
contentScale が 1.0 に設定されている場合、画像は 1 ポイントあたり 1 ピクセルで描画されます。2.0 に設定されている場合、画像は 1 ポイントあたり 2 ピクセルで描画されます。これが Retina スクリーンと呼ばれるものです。 (ピクセルとポイントの概念がよくわからない場合は、この章で後ほど説明します)。
これは、kCAGravityResizeAspect を使用する場合には影響しません。これは、レイヤーに合わせて画像を引き伸ばすだけであり、解像度の問題はまったく考慮されないためです。しかし、contentsGravity を kCAGravityCenter に設定すると (この値は画像を引き伸ばしません)、明らかな変化が見られます (図 2.3)
図 2.3 間違った contentScale 属性を使用した Retina 画像の表示
ご覧のとおり、私たちの雪だるまは少し大きいだけでなく、少し粒子が粗いです。 UIImage とは異なり、CGImage にはストレッチの概念がないためです。 UIImage クラスを使用して雪だるま画像を読み取ると、高品質の Retina バージョンの画像が読み取られます。ただし、CGImage を使用してレイヤーのコンテンツを設定すると、変換時にストレッチ係数が失われます。ただし、この問題は、contentScale を手動で設定することで解決できます (2.2 のリストと同様)。図 2.4 は、結果です
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| @implementation ViewController
- (void )viewDidLoad { [super viewDidLoad] // 画像をロード UIImage *image = [UIImage imageNamed:@"Snowman.png"]; // に直接追加しますビューのレイヤー self.layerView.layer.contents = (__bridge id)image.CGImage;
//画像を中央に配置します self.layerView.layer.contentsGravity = kCAGravityCenter; //contentScale を画像に合わせて設定します self.layerView.layer.contentsScale = image.scale }
@end |
図 2.4 同じ Retina 画像正しい contentScale を設定した後
コードを使用してホスト画像を処理する場合は、必ずレイヤーの contentScale プロパティを手動で設定することを忘れないでください。そうしないと、画像が Retina デバイスで正しく表示されません。コードは次のとおりです
これで、雪だるまは最終的に正しいサイズを表示しますが、何か他のことに気づいたかもしれません。彼はビューの境界を超えています。デフォルトでは、UIView は境界を超えるコンテンツまたはサブビューを描画します。これは CALayer でも同様です。 ... 2.5 MaskToBounds を使用してレイヤー コンテンツを構築する
contentRect
CALayer の contentRect プロパティを使用すると、レイヤー境界にホスト画像のサブドメインを表示できます。これには、画像の表示方法と伸縮方法が含まれるため、contentsGravity よりもはるかに柔軟です
境界やフレームとは異なり、contentsRect はポイントによって計算されません。単位座標は 0 から 1 の間で指定されます。相対値 (ピクセルとポイントは絶対値です)。したがって、それらは搭乗図のサイズに比例します。 iOS は次の座標系を使用します:
ポイント ?? iOS および Mac OS で最も一般的な座標系。ポイントは仮想ピクセルのようなもので、論理ピクセルとも呼ばれます。標準デバイスでは 1 ポイントは 1 ピクセルですが、Retina デバイスでは 1 ポイントは 2*2 ピクセルに相当します。 iOS では、Retina デバイスと通常のデバイスで一貫した視覚効果を実現するために、画面の座標計算システムとしてポイントを使用します。 ピクセル ?? 物理的なピクセル座標は画面レイアウトには使用されませんが、画像に対して相対的なものになります。 UIImage は画面解像度ソリューションであるため、サイズを測定するポイントを指定します。ただし、CGImage などの一部の低レベルの画像表現ではピクセルが使用されるため、Retina デバイスと通常のデバイスでは異なるサイズで表示されることに注意する必要があります。 単位 ?? 画像サイズやレイヤー境界に関連する表示の場合、単位座標はサイズが変わっても再調整する必要がない便利な測定値です。ユニット座標は OpenGL などのテクスチャ座標系でよく使用され、CoreAnimation でも使用されます。実際、contentsRect に負の原点や {1, 1} より大きいサイズを設定することも可能です。この場合、最も外側のピクセルが引き伸ばされて残りの領域が埋められます。
アプリ内のcontentsRectの最も興味深い点は、画像スプライトと呼ばれるその使用法です。ゲーム プログラミングの経験がある場合は、画像の画面上の位置を個別に変更できる画像ステッチの概念に精通している必要があります。ゲーム プログラミングはさておき、このテクニックは、ステッチされた画像の読み込みを指すためによく使用され、動画とは何の関係もありません。
通常、写真を結合した後、パッケージ化して 1 回の読み込みで 1 つの大きな写真に統合できます。異なる画像を複数回ロードする場合と比較して、メモリ使用量、ロード時間、レンダリング パフォーマンスなど、多くの利点が得られます。
2D ゲーム エンジンは、OpenGL を使用して画像を表示する Cocos2D のスプライシング テクノロジを使用します。しかし、通常の UIKit アプリでもステッチを使用できます。 contentRect を使用するだけです
まず、ステッチされたチャートが必要です?? 小さなステッチされた画像を含む大きな画像。図 2.7 に示すように:
次に、これらのステッチされた画像をアプリにロードして表示する必要があります。ルールは簡単です。通常どおり大きな画像をロードし、それを 4 つの別々のレイヤーのコンテンツに割り当て、各レイヤーの contentRect を設定して表示したくない部分を削除します。
プロジェクトには追加のビューが必要です。 (コードが多すぎるのを避けるため。Interface Builder を使用してそれらの場所にアクセスしますが、必要に応じてコード内でアクセスすることもできます)。リスト 2.3 に必要なコードを示し、図 2.8 にその結果を示します。
67
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
@interface ViewController ()
@property (非アトミック、弱い) IBOutlet UIView *coneView;
@property (非アトミック、弱い) IBOutlet UIView *shipView;
@property (非アトミック、弱い) IBOutlet UIView *iglooView;
@property (非アトミック、弱い) IBOutlet UIView *anchorView;
@end
@implementation ViewController
- (void)addSpriteImage:(UIImage *)image withContentRect:(CGRect)rect ?toLayer:(CALayer *)layer //set image
{
レイヤー.contents = (__bridge id)image.CGImage;
//コンテンツに合わせてスケールします
layer.contentsGravity = kCAGravityResizeAspect;
//set contentRect
layer.contentsRect = rect;
}
- (void)viewDidLoad
{
[super viewDidLoad]; // スプライト シートを読み込みます
UIImage *image = [UIImage imageNamed:@"Sprites.png"];
//igloo スプライトを設定します
[self addSpriteImage:image withContentRect:CGRectMake(0, 0, 0.5, 0.5) toLayer:self.iglooView.layer];
//円錐スプライトを設定します
[self addSpriteImage:image withContentRect:CGRectMake(0.5, 0, 0.5, 0.5) toLayer:self.coneView.layer];
//アンカースプライトを設定します
[self addSpriteImage:image withContentRect:CGRectMake(0, 0.5, 0.5, 0.5) toLayer:self.anchorView.layer];
//宇宙船のスプライトを設定します
[self addSpriteImage:image withContentRect:CGRectMake(0.5, 0.5, 0.5, 0.5) toLayer:self.shipView.layer];
}
@end
分割は、アプリにきちんとした読み込み方法を提供するだけでなく、読み込みパフォーマンスを効果的に向上させます (単一の大きな画像は複数の小さな画像よりも速く読み込まれます)。ただし、手動で配置した場合でも、それらは依然として存在します。すでに作成された製品や図面にサイズの変更やその他の変更を加える必要がある場合、間違いなく面倒になります。
Mac には、画像を自動的に結合できる商用ソフトウェアがいくつかあります。これらのツールは、結合された座標を含む XML または plist ファイルを自動的に生成し、結合された画像の使用を大幅に簡素化します。このファイルは画像と一緒にロードして、ステッチされたレイヤーごとに contentRect を設定できるため、開発者は位置を決めるコードを手動で記述する必要がありません。
これらのファイルは通常 OpenGL ゲームで使用されますが、いくつかの一般的なアプリでスプライシング テクノロジを使用することに興味がある場合は、LayerSprites (https://github.com/nicklockwood/LayerSprites) と呼ばれるオープン ソース ライブラリがあります。ステッチされたイメージを Cocos2D 形式で読み取り、通常の Core Animation レイヤーに表示します。
この章で紹介した最後のコンテンツ関連属性は、contentCenter です。名前を見ると、画像の位置に関連しているのではないかと思われるかもしれませんが、この名前は誤解を招きます。 contentCenter は実際には、レイヤー上の固定境界線と伸縮可能な領域を定義する CGRect です。 contentCenter の値を変更しても、ホスト イメージの表示には影響しません。レイヤーのサイズを変更しない限り、その効果は現れません。
デフォルトでは、contentsCenter は {0, 0, 1, 1} です。これは、サイズ (contentsGravity によって決定される) が変更されると、ホスティング イメージが均等に引き伸ばされることを意味します。ただし、原点の値を増やしてサイズを減らすとします。画像の周りに枠線を作成していきます。図 2.9 は、contentsCenter を {0.25, 0.25, 0.5, 0.5} に設定した場合の効果を示しています。
図 2.9 contentCenter の例
これは、自由にサイズを変更でき、境界線は依然として連続していることを意味します。これが動作する効果は、UIImage の -resizableImageWithCapInsets: メソッドと非常に似ていますが、Core Graphics の実行中に描画されるグラフィックスを含め、任意のホスト イメージに適用できる点が異なります (この章で後述します)。
図 2.10 異なる contentCenter を使用した同じ画像
リスト 2.4 は、これらの伸縮可能なビューの作成方法を示しています。ただし、contentsCenter のもう 1 つの優れた機能は、コードをまったく記述せずに Interface Builder で構成できることです。図 2.11 に示すように
リスト 2.4 contentCenter で伸縮可能なビューを設定する
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
@interface ViewController ()
@prop erty (非アトミック、弱い) IBOutlet UIView *button1 @property (非アトミック、弱い) ) IBOutlet UIView *ボタン 2;
@end
@implementation ViewController
- (void)addStretchableImage:(UIImage *)image withContentCenter:(CGRect)rect toLayer:(CALayer *)layer { //画像を設定 layer.contents = (__bridge id)image.CGImage;
//set contentCenter layer.contentsCenter = rect; }
- (void)viewDidLoad { [super viewDidLoad]; // ボタン画像を読み込みます UIImage *image = [UIImage imageNamed:@"Button.png"];
//ボタン 1 を設定 [self addStretchableImage:image withContentCenter:CGRectMake(0.25, 0.25, 0.5, 0.5) toLayer:self.button1.layer];
//ボタン 2 を設定 [self addStretchableImage:image withContentCenter:CGRectMake(0.25, 0.25, 0.5, 0.5) toLayer:self.button2.layer]; }
@end |
図 2.11 Interface Builder 検出ウィンドウを使用して contentCenter プロパティを制御する
CGImage 値をコンテンツに割り当てることは、ホスティング イメージを設定する唯一の方法ではありません。 Core Graphics を使用して、搭乗マップを直接描画することもできます。 UIViewを継承し、-drawRect:メソッドを実装することで描画をカスタマイズできます。
-drawRect: メソッドにはデフォルトの実装がありません。UIView の場合、ホスティング画像は必要なく、それが単調な色であるか画像のインスタンスであるかは関係ないからです。 UIView は、-drawRect: メソッドが呼び出されたことを検出すると、ビュー サイズに contentScale を乗算した値に等しいピクセル寸法でバッキング イメージをビューに割り当てます。
グラフィックスをホストする必要がない場合は、このメソッドを作成しないでください。これにより、CPU リソースとメモリが無駄に消費されます。これが、Apple が推奨している理由です。カスタム描画タスクがない場合は、メソッドを作成しないでください。サブクラス -drawRect: メソッド内の空のメソッド。
ビューが画面に表示されると、-drawRect: メソッドが自動的に呼び出されます。 -drawRect: メソッドのコードは Core Graphics を使用してホスト イメージを描画し、コンテンツは更新が必要になるまでキャッシュされます (通常は、パフォーマンスに影響するプロパティ値が変更された場合でも、開発者が -setNeedsDisplay メソッドを呼び出すため) . 、bounds プロパティなど、一部のビュー タイプは自動的に再描画されます。 -drawRect: メソッドは UIView メソッドですが、実際には、再描画作業を調整し、結果のイメージを保存するのは基礎となる CALayer です。
CALayer には、CALayerDelegate プロトコルを実装するオプションのデリゲート属性があります。CALayer がコンテンツ固有の情報を必要とする場合、プロトコルから情報を要求します。 CALayerDelegate は非公式のプロトコルです。実際、クラス内で参照できる CALayerDelegate @protocol が存在しないことを意味します。呼び出したいメソッドを呼び出すだけで、残りは CALayer が自動的に実行します。 (デリゲート属性は型 ID として宣言され、すべてのデリゲート メソッドはオプションです)。
再描画する必要がある場合、CALayer はプロキシに、表示する背景イメージを与えるように要求します。これは、次のメソッドを呼び出すことで実行されます。
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
@implementation ViewController - (void)viewDidLoad { | [super viewDidLoad];
//サブレイヤーを作成 CALayer *blueLayer = [CALayer レイヤー] = CGRectMake(50.0f, 50.0f, 100.0f, 100.0f); ブルーレイヤー.backgroundColor = [UIColor blueColor].CGColor;
// コントローラーをレイヤーデリゲートとして設定します blueLayer.delegate = self;
// レイヤーの背景画像が正しいスケールを使用することを確認します blueLayer.contentsScale = [ UIScreen mainScreen].scale; // ビューにレイヤーを追加 [self.layerView.layer addSublayer:blueLayer] // レイヤーを強制的に再描画 }
- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx { //太い赤い円を描画します CGContextSetLineWidth(ctx, 10.0f) CGContextSet StrokeColorWithColor(ctx, [UIColor redColor ]); .CGColor); CGContextStrokeEllipseInRect(ctx,layer.bounds);
図 2.12 レイヤーを描画するための CALayerDelegate の実装 いくつかの興味深いことに注目してください: blueLayer で -display を明示的に呼び出しました。 UIView とは異なり、CALayer はレイヤーが画面に表示されるときにそのコンテンツを自動的に再描画しません。再描画の決定は開発者に委ねられます。 MaskToBounds プロパティを使用しなかったにもかかわらず、描画された円は境界に沿ってクリップされました。これは、CALayerDelegate を使用してホスティング イメージを描画する場合、境界を越えてコンテンツを描画することがサポートされていないためです。これで、CALayerDelegate を理解し、その使用方法がわかりました。ただし、別のレイヤーを作成しない限り、CALayerDelegate プロトコルを使用する機会はほとんどありません。 UIView がホスト層を作成するときに、層のデリゲートをそれ自体に自動的に設定し、-displayLayer: の実装を提供するため、すべての問題は解決されます。 ビューをホストするレイヤーを使用する場合、ホストされた画像を描画するために -displayLayer: メソッドと -drawLayer:inContext: メソッドを実装する必要はありません。通常のアプローチは、UIView の -drawRect: メソッドを実装することです。再描画が必要な場合の -display メソッドの呼び出しなど、残りの作業は UIView が実行します。 概要この章では、ボーディング グラフといくつかの関連プロパティを紹介します。画像を表示および配置する方法、表示に平坦化手法を使用する方法、および CALayerDelegate と Core Graphics を使用してレイヤー コンテンツを描画する方法を学習しました。 |