Xamarin.Mac のイメージ
この記事は、Xamarin.Mac アプリケーションの画像とアイコンの操作について説明しています。 アプリケーションのアイコンを作成するために必要な画像の作成と管理、C# コードと Xcode の Interface Builder の両方での画像の使用について説明しています。
概要
Xamarin.Mac アプリケーションで C# と .NET を使用している場合、Objective-C と Xcode で開発者が使用しているのと同じイメージとアイコン ツールにアクセスできます。
macOS (旧称 Mac OS X) アプリケーション内でイメージ アセットを使用する方法はいくつかあります。 アプリケーションの UI の一部として単にイメージを表示することから、ツール バーやソース リスト項目などの UI コントロールに割り当てたり、アイコンを提供したりすることまで、Xamarin.Mac では、次のように macOS アプリケーションに優れたアートワークを簡単に追加できます。
- UI 要素 - イメージは背景として、またはアプリケーションの一部としてイメージ ビュー (
NSImageView
) に表示できます。 - ボタン - イメージはボタン (
NSButton
) に表示できます。 - イメージ セル - テーブル ベースのコントロール (
NSTableView
またはNSOutlineView
) の一部として、イメージ セル (NSImageCell
) でイメージを使用できます。 - ツール バーの項目 - イメージは、イメージ ツールバーの項目 (
NSToolbarItem
) としてツールバー (NSToolbar
) に追加できます。 - ソース リスト アイコン - ソース リスト (特別に書式設定された
NSOutlineView
) の一部として。 - アプリ アイコン - 一連のイメージを
.icns
のセットにグループ化し、アプリケーションのアイコンとして使用できます。 詳細については、アプリケーション アイコンのドキュメントを参照してください。
さらに、macOS には、アプリケーション全体で使用できる定義済みのイメージのセットが用意されています。
この記事では、Xamarin.Mac アプリケーションでのイメージとアイコンの使用の基本について説明します。 この記事で使用する主要な概念と手法については、まず Hello Mac の記事、特に「Xcode と Interface Builder の概要」および「アウトレットとアクション」のセクションを参照することを強くお勧めします。
Xamarin.Mac プロジェクトへのイメージの追加
Xamarin.Mac アプリケーションで使用するイメージを追加する場合、開発者がプロジェクトのソースにイメージ ファイルを含めることができる場所と方法はいくつかあります。
- メイン プロジェクト ツリー [非推奨] - イメージをプロジェクト ツリーに直接追加できます。 メイン プロジェクト ツリーに格納されているイメージをコードから呼び出す場合、フォルダーの場所は指定されません。 (例:
NSImage image = NSImage.ImageNamed("tags.png");
)。 - Resources フォルダー [非推奨] - Resources フォルダーは特別なフォルダーで、アイコン、起動画面、一般的なイメージ (または開発者が追加するその他のイメージまたはファイル) など、アプリケーションのバンドルの一部となるファイル用のフォルダーです。 メイン プロジェクト ツリーに格納されているイメージと同様に、Resources フォルダーに格納されているイメージをコードから呼び出す場合、フォルダーの場所は指定されません。 (例:
NSImage.ImageNamed("tags.png")
)。 - カスタム フォルダーまたはサブフォルダー [非推奨] - 開発者は、プロジェクトのソース ツリーにカスタム フォルダーを追加し、そこにイメージを格納できます。 ファイルを追加する場所をサブフォルダーに入れ子にして、プロジェクトをさらに整理できます。 たとえば、開発者がプロジェクトに
Card
フォルダーを追加し、そのフォルダーにHearts
のサブ フォルダーを追加した場合、そのHearts
フォルダーにイメージ Jack.png を格納すると、NSImage.ImageNamed("Card/Hearts/Jack.png")
は実行時にそのイメージを読み込みます。 - アセット カタログ イメージ セット [推奨] - OS X El Capitan で追加されたアセット カタログ イメージ セットには、アプリケーションのさまざまなデバイスとスケール ファクターをサポートするために必要なイメージのすべてのバージョンまたは表現が含まれています。 イメージ アセットのファイル名 (@1x、@2x) に依存する代わりに。
アセット カタログ イメージ セットへのイメージの追加
前述のように、アセット カタログ イメージ セットには、アプリケーションのさまざまなデバイスとスケール ファクターをサポートするために必要なイメージのすべてのバージョンまたは表現が含まれています。 イメージ セットは、イメージ アセットのファイル名 (上記の「解像度に依存しないイメージとイメージ命名法」を参照) に依存する代わりに、アセット エディターを使用して、どのイメージがどのデバイスや解像度に属するかを指定します。
Solution Pad で Assets.xcassets ファイルをダブルクリックして、編集用に開きます。
[資産リスト] を右クリックし、[新しい画像セット] を選択します。
新しい画像セットを選択すると、エディターが表示されます。
ここから、必要なさまざまなデバイスと解像度ごとにイメージをドラッグできます。
[資産リスト] で新しいイメージ セットの名前をダブルクリックして編集します。
イメージ セットに特殊な Vector クラスが追加されました。これにより、個々のビットマップファイルをさまざまな解像度の含める代わりに、PDF 形式のベクター イメージをカセットに含めることができるようになりました。 このメソッドを使用すると、@1x 解像度のベクター ファイル (ベクター PDF ファイルとして書式設定されたもの) を 1 つ提供するだけで、そのファイルの @2x バージョンと @3x バージョンがコンパイル時に生成され、アプリケーションのバンドルに含まれます。
たとえば、解像度が 150px x 150px のアセット カタログのベクターとして MonkeyIcon.pdf
ファイルを含める場合、コンパイル時に、次のビットマップ アセットが最終的なアプリ バンドルに含まれます。
- MonkeyIcon@1x.png - 150px x 150px の解像度。
- MonkeyIcon@2x.png - 300px x 300px の解像度。
- MonkeyIcon@3x.png - 450px x 450px の解像度。
Asset Catalogs で PDF ベクター画像を使用する場合は、次の点を考慮する必要があります。
- PDF はコンパイル時にビットマップにラスター化され、そのビットマップは最終的なアプリケーションに同梱されるため、これは完全なベクター サポートではありません。
- アセット カタログで一度設定したイメージのサイズを調整することはできません。 イメージのサイズを (コード内で、または自動レイアウトとサイズ クラスを使用して) 変更しようとすると、他のビットマップと同様にイメージが歪みます。
Xcode の Interface Builder でイメージ セットを使用する場合は、[属性インスペクター]のドロップダウン リストからセットの名前を選択するだけで使用できます。
新しいアセット コレクションの追加
アセット カタログでイメージを使用する場合は、Assets.xcassets コレクションにすべてのイメージを追加するのではなく、新しいコレクションを作成したいと考える場合があるかと思います。 たとえば、オンデマンド リソースを設計する場合などです。
新しいアセット カタログをプロジェクトに追加するには、次の手順を実施します。
Solution Pad でプロジェクトを右クリックし、[追加]>[新しいファイル...] を選択します。
[Mac]>[アセット カタログ] の順に選択して、コレクションの名前を入力後、[新規] ボタンをクリックします。
ここから、プロジェクトに自動的に含まれる既定の Assets.xcassets コレクションと同じように、コレクションを使用できます。
リソースへのイメージの追加
重要
macOS アプリでイメージを使用するこの方法は、Apple によって非推奨となりました。 代わりに、アセット カタログ イメージ セットを使用してアプリのイメージを管理する必要があります。
Xamarin.Mac アプリケーション (C# コードまたは Interface Builder) でイメージ ファイルを使用する前に、そのファイルをプロジェクトの Resources フォルダーにバンドル リソースとして含める必要があります。 プロジェクトにファイルを追加するには、次の操作を行います。
Solution Pad でプロジェクトの Resources フォルダーを右クリックし、[追加]>[ファイルの追加...] を選択します。
[ファイルの追加] ダイアログ ボックスで、プロジェクトに追加するイメージ ファイルを選択し、[ビルド アクションのオーバーライド] に
BundleResource
を選択して、[開く] ボタンをクリックします。ファイルがまだ Resources フォルダーにない場合は、ファイルをコピーするか、移動するか、またはリンクするかどうかを確認するメッセージが表示されます。 ニーズに応じてその 3 つより選択してください。通常はコピーを選択します。
新しいファイルがプロジェクトに含まれて、使用するために読み取られます。
必要なイメージ ファイルに対してこのプロセスを繰り返します。
Xamarin.Mac アプリケーションでは、任意の png、jpg、または pdf ファイルをソース イメージとして使用できます。 次のセクションでは、Retina ベースの Mac をサポートするために、イメージとアイコンの高解像度バージョンを追加する方法について説明します。
重要
[Resources] フォルダーにイメージを追加する場合は、[ビルド アクションのオーバーライド] は [既定] のままで構いません。 このフォルダーの既定のビルド アクションは BundleResource
です。
すべてのアプリ グラフィック リソースの高解像度バージョンを提供する
Xamarin.Mac アプリケーションに追加するグラフィック アセット (アイコン、カスタム コントロール、カスタム カーソル、カスタム アートワークなど) には、標準解像度バージョンに加えて高解像度バージョンが必要です。 これは、Retina ディスプレイを搭載した Mac コンピュータでアプリケーションを実行する際に、最高の外観を実現するために必要なものとなります。
@2x 名前付け規則を採用する
重要
macOS アプリでイメージを使用するこの方法は、Apple によって非推奨となりました。 代わりに、アセット カタログ イメージ セットを使用してアプリのイメージを管理する必要があります。
イメージの標準バージョンと高解像度バージョンを作成する場合、Xamarin.Mac プロジェクトにイメージ ペアを含めるときに、イメージ ペアの次の名前付け規則に従ってください。
- 標準解像度 - イメージ名.ファイル名拡張子 (例: tags.png)
- 高解像度 - ImageName@2x.filename拡張子 (例: tags@2x.png)
プロジェクトに追加すると、次のように表示されます。
Interface Builder で UI 要素にイメージが割り当てられている場合は、イメージ名.ファイル名拡張子形式 (例: tags.png) でファイルを選択するだけです。 C# コードでイメージを使用する場合も同様に、イメージ名.ファイル名拡張子形式を選択します。
Xamarin.Mac アプリケーションを Mac 上で実行すると、イメージ名.ファイル名拡張子形式のイメージが標準解像度ディスプレイで使用され、ImageName@2x.filename拡張子イメージは自動的に Retina ディスプレイベースのMac上で選択されます。
Interface Builder でのイメージの使用
Xamarin.Mac プロジェクトの Resources フォルダーに追加し、ビルド アクションを BundleResource に設定したイメージ リソースは、Interface Builder に自動的に表示され、UI 要素の一部として選択できます (イメージを処理する場合)。
Interface Builder でイメージを使用するには、次の操作を行います。
BundleResource
のビルド アクションを使用して、Resources フォルダーにイメージを追加します。Main.storyboard ファイルをダブルクリックして、Interface Builder で編集用に開きます:
イメージを取り込む UI 要素 (例: 画像ツール バー項目など) をデザイン サーフェイスにドラッグします。
[イメージ名] ドロップダウンで、Resources フォルダーに追加したイメージを選択します。
選択したイメージがデザイン サーフェイスに表示されます。
変更内容を保存し、Visual Studio for Mac に戻って Xcode と同期します。
上記の手順は、属性インスペクターでイメージ プロパティを設定できる UI 要素に対して機能します。 ここでも、画像ファイルの @2x バージョンを含める場合は、Retina ディスプレイ ベースの Mac 上で自動的に使用されます。
重要
[イメージ名] ドロップダウンにイメージがない場合は、Xcode の .storyboard プロジェクトを閉じて、Visual Studio for Mac から再度開き直してください。 再度開き直してもイメージがまだない場合は、ビルド アクションが BundleResource
であることと、イメージが Resources フォルダーに追加されていることを確認してください。
C# コードでのイメージの使用
Xamarin.Mac アプリケーションで C# コードを使用してイメージをメモリに読み込む場合、イメージは NSImage
オブジェクトに格納されます。 イメージ ファイルが Xamarin.Mac アプリケーション バンドル (リソースに含まれている) に含まれている場合は、次のコードを使用してイメージを読み込みます。
NSImage image = NSImage.ImageNamed("tags.png");
上記のコードでは、NSImage
クラスの静的 ImageNamed("...")
メソッドを使用して、指定されたイメージを Resources フォルダーからメモリに読み込みます。イメージが見つからない場合は、null
が返されます。 Interface Builder で割り当てられたイメージの場合と同様に、イメージ ファイルの @2x バージョンを含める場合は、Retina ディスプレイ ベースの Mac 上で自動的に使用されます。
アプリケーションのバンドルの外部 (Mac ファイル システムから) イメージを読み込むには、次のコードを使用します。
NSImage image = new NSImage("/Users/KMullins/Documents/photo.jpg")
テンプレート イメージの使用
macOS アプリの設計に基づいて、配色の変更 (ユーザー設定に基づくなど) に合わせて、ユーザー インターフェイス内のアイコンまたはイメージをカスタマイズする必要がある場合があります。
この効果を実現するには、イメージ アセットのレンダリング モードを[テンプレート イメージ] に切り替えます。
Xcode の Interface Builder から、UI コントロールにイメージ アセットを割り当てます。
または、必要に応じてコードでイメージ ソースを設定することもできます。
MyIcon.Image = NSImage.ImageNamed ("MessageIcon");
View Controller に次のパブリック関数を追加します。
public NSImage ImageTintedWithColor(NSImage sourceImage, NSColor tintColor)
=> NSImage.ImageWithSize(sourceImage.Size, false, rect => {
// Draw the original source image
sourceImage.DrawInRect(rect, CGRect.Empty, NSCompositingOperation.SourceOver, 1f);
// Apply tint
tintColor.Set();
NSGraphics.RectFill(rect, NSCompositingOperation.SourceAtop);
return true;
});
重要
特に macOS Mojave でダーク モードが出現する場合は、カスタムレンダリングされた NSImage
オブジェクトを評価するときに LockFocus
API を回避することが重要です。 このようなイメージは静的なものとなり、外観や表示密度の変更を考慮して自動的に更新されることはありません。
上記のハンドラーベースのメカニズムを採用することで、たとえば NSImage
が NSImageView
でホストされている場合に、動的条件に対する再レンダリングが自動的に行われます。
最後に、テンプレート イメージを色付けするには、着色するイメージに対してこの関数を呼び出します。
MyIcon.Image = ImageTintedWithColor (MyIcon.Image, NSColor.Red);
テーブル ビューでのイメージの使用
NSTableView
のセルの一部として画像を含めるには、テーブル ビューの NSTableViewDelegate's
GetViewForItem
メソッドによってデータが返される方法を変更して、一般的なNSTextField
ではなくNSTableCellView
を使用する必要があります。 次に例を示します。
public override NSView GetViewForItem (NSTableView tableView, NSTableColumn tableColumn, nint row)
{
// This pattern allows you reuse existing views when they are no-longer in use.
// If the returned view is null, you instance up a new view
// If a non-null view is returned, you modify it enough to reflect the new data
NSTableCellView view = (NSTableCellView)tableView.MakeView (tableColumn.Title, this);
if (view == null) {
view = new NSTableCellView ();
if (tableColumn.Title == "Product") {
view.ImageView = new NSImageView (new CGRect (0, 0, 16, 16));
view.AddSubview (view.ImageView);
view.TextField = new NSTextField (new CGRect (20, 0, 400, 16));
} else {
view.TextField = new NSTextField (new CGRect (0, 0, 400, 16));
}
view.TextField.AutoresizingMask = NSViewResizingMask.WidthSizable;
view.AddSubview (view.TextField);
view.Identifier = tableColumn.Title;
view.TextField.BackgroundColor = NSColor.Clear;
view.TextField.Bordered = false;
view.TextField.Selectable = false;
view.TextField.Editable = true;
view.TextField.EditingEnded += (sender, e) => {
// Take action based on type
switch(view.Identifier) {
case "Product":
DataSource.Products [(int)view.TextField.Tag].Title = view.TextField.StringValue;
break;
case "Details":
DataSource.Products [(int)view.TextField.Tag].Description = view.TextField.StringValue;
break;
}
};
}
// Tag view
view.TextField.Tag = row;
// Setup view based on the column selected
switch (tableColumn.Title) {
case "Product":
view.ImageView.Image = NSImage.ImageNamed ("tags.png");
view.TextField.StringValue = DataSource.Products [(int)row].Title;
break;
case "Details":
view.TextField.StringValue = DataSource.Products [(int)row].Description;
break;
}
return view;
}
ここには興味深い行がいくつかあります。 まず、イメージを含める列については、必要なサイズと場所の NSImageView
を新規で作成します。また、NSTextField
を新規で作成し、イメージを使用しているかどうかに基づいて既定の位置に配置します。
if (tableColumn.Title == "Product") {
view.ImageView = new NSImageView (new CGRect (0, 0, 16, 16));
view.AddSubview (view.ImageView);
view.TextField = new NSTextField (new CGRect (20, 0, 400, 16));
} else {
view.TextField = new NSTextField (new CGRect (0, 0, 400, 16));
}
次に、新しい Image View とテキスト フィールドを親 NSTableCellView
に含める必要があります。
view.AddSubview (view.ImageView);
...
view.AddSubview (view.TextField);
...
最後に、テキスト フィールドがテーブル ビュー セルで縮小したり拡大できることを伝える必要があります。
view.TextField.AutoresizingMask = NSViewResizingMask.WidthSizable;
出力例:
テーブル ビューの使用に関する詳細情報については、テーブル ビューのドキュメントを参照してください。
アウトライン ビューでの画像の使用
NSOutlineView
のセルの一部として画像を含めるには、アウトライン ビューの NSTableViewDelegate's
GetView
メソッドによってデータが返される方法を変更して、一般的なNSTextField
の代わりにNSTableCellView
を使用する必要があります。 次に例を示します。
public override NSView GetView (NSOutlineView outlineView, NSTableColumn tableColumn, NSObject item) {
// Cast item
var product = item as Product;
// This pattern allows you reuse existing views when they are no-longer in use.
// If the returned view is null, you instance up a new view
// If a non-null view is returned, you modify it enough to reflect the new data
NSTableCellView view = (NSTableCellView)outlineView.MakeView (tableColumn.Title, this);
if (view == null) {
view = new NSTableCellView ();
if (tableColumn.Title == "Product") {
view.ImageView = new NSImageView (new CGRect (0, 0, 16, 16));
view.AddSubview (view.ImageView);
view.TextField = new NSTextField (new CGRect (20, 0, 400, 16));
} else {
view.TextField = new NSTextField (new CGRect (0, 0, 400, 16));
}
view.TextField.AutoresizingMask = NSViewResizingMask.WidthSizable;
view.AddSubview (view.TextField);
view.Identifier = tableColumn.Title;
view.TextField.BackgroundColor = NSColor.Clear;
view.TextField.Bordered = false;
view.TextField.Selectable = false;
view.TextField.Editable = !product.IsProductGroup;
}
// Tag view
view.TextField.Tag = outlineView.RowForItem (item);
// Allow for edit
view.TextField.EditingEnded += (sender, e) => {
// Grab product
var prod = outlineView.ItemAtRow(view.Tag) as Product;
// Take action based on type
switch(view.Identifier) {
case "Product":
prod.Title = view.TextField.StringValue;
break;
case "Details":
prod.Description = view.TextField.StringValue;
break;
}
};
// Setup view based on the column selected
switch (tableColumn.Title) {
case "Product":
view.ImageView.Image = NSImage.ImageNamed (product.IsProductGroup ? "tags.png" : "tag.png");
view.TextField.StringValue = product.Title;
break;
case "Details":
view.TextField.StringValue = product.Description;
break;
}
return view;
}
ここには興味深い行がいくつかあります。 まず、イメージを含める列については、必要なサイズと場所の NSImageView
を新規で作成します。また、NSTextField
を新規で作成し、イメージを使用しているかどうかに基づいて既定の位置に配置します。
if (tableColumn.Title == "Product") {
view.ImageView = new NSImageView (new CGRect (0, 0, 16, 16));
view.AddSubview (view.ImageView);
view.TextField = new NSTextField (new CGRect (20, 0, 400, 16));
} else {
view.TextField = new NSTextField (new CGRect (0, 0, 400, 16));
}
次に、新しい Image View とテキスト フィールドを親 NSTableCellView
に含める必要があります。
view.AddSubview (view.ImageView);
...
view.AddSubview (view.TextField);
...
最後に、テキスト フィールドがテーブル ビュー セルで縮小したり拡大できることを伝える必要があります。
view.TextField.AutoresizingMask = NSViewResizingMask.WidthSizable;
出力例:
アウトライン ビューの使用に関する詳細詳細については、アウトライン ビューのドキュメントを参照してください。
まとめ
この記事では、Xamarin.Mac アプリケーションでのイメージとアイコンの使用について詳しく説明しました。 Xcode の Interface Builder でイメージとアイコンを使用する方法、C# コードでイメージとアイコンを使用する方法など、イメージのさまざまな種類と使用方法について説明しました。