Xamarin.Mac 中的影像

本文涵蓋在 Xamarin.Mac 應用程式中使用影像和圖示。 其描述如何建立和維護建立應用程式圖示及使用 C# 程式代碼和 Xcode 介面產生器中的影像所需的映像。

概觀

在 Xamarin.Mac 應用程式中使用 C# 和 .NET 時,您可以存取開發人員在 和 XcodeObjective-C運作的相同影像和圖示工具。

在macOS(先前稱為Mac OS X)應用程式中,有數種方式可以使用映像資產。 從將影像顯示為應用程式 UI 的一部分,將它指派給 UI 控件,例如工具列或來源清單專案,到提供圖示,Xamarin.Mac 可讓您輕鬆地以下列方式將絕佳的藝術品新增至 macOS 應用程式:

  • UI 元素 - 影像可以在影像檢視中顯示為背景或應用程式的一部分。NSImageView
  • 按鈕 - 影像可以顯示在按鈕中(NSButton)。
  • Image Cell - 做為數據表型控件的一部分(NSTableViewNSOutlineView),影像可用於 Image Cell (NSImageCell)。
  • 工具列專案 - 影像可以新增至工具列 (NSToolbar) 作為影像工具列專案 (NSToolbarItem)。
  • 來源清單圖示 - 作為來源清單的一部分(特別格式化 NSOutlineView)。
  • 應用程式圖示 - 一系列影像可以分組成一 .icns 組,並用來作為應用程式的圖示。 如需詳細資訊,請參閱我們的 應用程式圖示 檔。

此外,macOS 提供一組預先定義的影像,可在您的應用程式中使用。

An example run of the app

在本文中,我們將討論在 Xamarin.Mac 應用程式中使用影像和圖示的基本概念。 強烈建議您先完成 Hello,Mac 文章,特別是 Xcode 和 Interface Builder 和 Outlets 和 Actions 簡介小節,因為它涵蓋我們將在本文中使用的重要概念和技術。

將影像新增至 Xamarin.Mac 專案

新增影像以用於 Xamarin.Mac 應用程式中時,開發人員可以透過數個位置與方式將圖像檔包含至專案的來源:

  • 主要專案樹狀目錄 [已淘汰] - 影像可以直接新增至專案樹狀結構。 從程式代碼呼叫儲存在主要專案樹狀結構中的影像時,不會指定任何資料夾位置。 例如: NSImage image = NSImage.ImageNamed("tags.png");
  • Resources 資料夾 [已淘汰] - 特殊 Resources 資料夾適用於任何將成為應用程式套件組合一部分的檔案,例如圖示、啟動畫面或一般影像(或任何其他開發人員想要新增的影像或檔案)。 從程式代碼呼叫儲存在 Resources 資料夾中的影像時,就像儲存在主要專案樹狀結構中的影像一樣,不會指定任何資料夾位置。 例如: NSImage.ImageNamed("tags.png")
  • 自訂資料夾或子資料夾 [已被取代] - 開發人員可以將自定義資料夾新增至專案來源樹狀結構,並將影像儲存在那裡。 新增檔案的位置可以巢狀於子資料夾中,以進一步協助組織專案。 例如,如果開發人員將資料夾新增至專案,並將的Hearts子資料夾新增Card至該資料夾,則將影像Jack.png儲存在Hearts資料夾中,NSImage.ImageNamed("Card/Hearts/Jack.png")則會在運行時間載入映像。
  • 資產類別目錄映像集 [慣用] - 在 OS X El Capitan 中新增, 資產類別目錄映射集 包含支援各種裝置和應用程式縮放因數所需之映射的所有版本或表示法。 而不是依賴映像資產檔名(@1x@2x)。

將映像新增至資產類別目錄映像集

如上所述, 資產類別目錄映射集 包含支援各種裝置和應用程式縮放比例所需之映像的所有版本或表示法。 影像集不使用依賴影像資產檔名(請參閱上述的解析度獨立影像和影像名詞), 影像集 會使用資產編輯器來指定哪些影像屬於哪一個裝置和/或解析度。

  1. 在 Solution Pad,按兩下 Assets.xcassets 檔案以開啟它以進行編輯:

    Selecting the Assets.xcassets

  2. 以滑鼠右鍵按兩下 [資產清單 ],然後選取 [ 新增映射集]:

    Adding a new image set

  3. 選取新的映像集,並顯示編輯器:

    Selecting the new image set

  4. 從這裡,我們可以針對每個所需的不同裝置和解析度拖曳影像。

  5. 按兩下 [資產清單] 中的新映像集 [名稱] 加以編輯:

    Editing the image set name

新增至影像集的特殊 Vector 類別,可讓我們在大小寫集中包含 PDF 格式化的向量影像,而改為在不同的解析度中包含個別位圖檔案。 使用此方法時,您會提供單一向量檔案來進行 @1x 解析(格式化為向量 PDF 檔案),而 檔案的@2x@3x 版本將會在編譯階段產生,並包含在應用程式的套件組合中。

The image set editor interface

例如,如果您以 150px x 150px 解析度作為資產類別目錄向量包含 MonkeyIcon.pdf 檔案,則編譯後,最終應用程式套件組合中會包含下列位圖資產:

  1. MonkeyIcon@1x.png - 150px x 150px 解析度。
  2. MonkeyIcon@2x.png - 300px x 300px 解析度。
  3. MonkeyIcon@3x.png - 450px x 450px 解析度。

在資產目錄中使用 PDF 向量影像時,應考慮下列事項:

  • 這不是完整的向量支持,因為 PDF 會在編譯時期點陣化為位圖,而且點陣化為最終應用程式中。
  • 在資產目錄中設定映射之後,您無法調整影像的大小。 如果您嘗試調整影像的大小(在程序代碼中,或是使用自動配置和大小類別),則影像會像任何其他點陣圖一樣扭曲。

在 Xcode 的 Interface Builder 中使用映射集時,您只要從屬性偵測器中的下拉式清單中選取集合的名稱:

Selecting an image set in Xcode's Interface Builder

新增資產集合

當您在資產目錄中使用影像時,有時候您可能會想要建立新的集合,而不是將所有映像新增至 Assets.xcassets 集合。 例如,設計隨選資源時。

若要將新的資產目錄新增至您的專案:

  1. 以滑鼠右鍵按下 Solution Pad 中的項目,然後選取 [新增>檔案...

  2. 選取 [ Mac>資產目錄],輸入 集合的 [名稱 ],然後按下 [ 新增 ] 按鈕:

    Adding a new Asset Catalog

您可以從這裡使用集合,就像項目中自動包含的預設 Assets.xcassets 集合一樣。

將影像新增至資源

重要

在 macOS 應用程式中使用影像的這個方法已被 Apple 取代。 您應該改用 資產類別目錄映像集 來管理應用程式的映像。

您必須先將專案 [資源] 資料夾中的 [資源] 資料夾包含套件組合資源,才能在 Xamarin.Mac 應用程式中使用影像檔案(在 C# 程式代碼中或從介面產生器中)。 若要將檔案新增至專案,請執行下列動作:

  1. 以滑鼠右鍵按下 Solution Pad專案中的 [資源] 資料夾,然後選取 [新增>檔案...] :

    Adding a file

  2. 從 [ 新增檔案 ] 對話框中,選取要新增至專案的影像檔案,選取 BundleResource[覆寫建置] 動作 ,然後按兩下 [ 開啟 ] 按鈕:

    Selecting the files to add

  3. 如果檔案尚未位於 Resources 資料夾中,系統會詢問您是否要 複製移動連結 檔案。 挑選符合您需求的每個專案,通常是 複製

    Selecting the add action

  4. 新檔案會包含在專案中,並讀取以供使用:

    The new image files added to the Solution Pad

  5. 針對任何所需的圖像檔重複此程式。

您可以使用任何 png、jpg 或 pdf 檔案作為 Xamarin.Mac 應用程式中的來源影像。 在下一節中,我們將探討如何新增影像和圖示的高解析度版本,以支援 Retina 型 Mac。

重要

如果您要將映像新增至 Resources 資料夾,您可以將 [ 覆寫建置] 動作 設定為 [預設值]。 此資料夾的預設建置動作為 BundleResource

提供所有應用程式圖形資源的高解析度版本

您新增至 Xamarin.Mac 應用程式的任何圖形資產(圖示、自定義控件、自定義游標、自定義圖稿等)除了其標準解析度版本之外,還需要有高解析度版本。 這是必要專案,讓您的應用程式在配備 Retina Display 的 Mac 計算機上執行時會看起來最佳。

採用@2x命名慣例

重要

在 macOS 應用程式中使用影像的這個方法已被 Apple 取代。 您應該改用 資產類別目錄映像集 來管理應用程式的映像。

當您建立映像的標準和高解析度版本時,請在 Xamarin.Mac 專案中納入映射組時,遵循映像組的這個命名慣例:

  • Standard-Resolution - ImageName.filename-extension (範例:tags.png
  • 高解析度 - ImageName@2x.filename延伸模組 (範例: ) tags@2x.png

新增至專案時,它們會顯示如下:

The image files in the Solution Pad

將影像指派給 Interface Builder 中的 UI 元素時,您只需在 ImageName挑選檔案即可。filename-extension 格式(範例:tags.png)。 在 C# 程式代碼中使用影像時,您會在 ImageName挑選檔案。filename-extension 格式。

當您在 Mac 上執行 Xamarin.Mac 應用程式時,ImageName。filename-extension format image will used on Standard Resolution Display, ImageName@2x.filenamethe -extension image will automatically picked on Retina Display bases Macs.

在介面產生器中使用影像

您在 Xamarin.Mac 專案中新增至 Resources 資料夾的任何影像資源,並將建置動作設定為 BundleResource 會自動顯示在介面產生器中,並可選取為 UI 元素的一部分(如果處理影像)。

若要在介面產生器中使用影像,請執行下列動作:

  1. 使用 的 [建置動作BundleResource] 將映射新增至 Resources 資料夾

    An image resource in the Solution Pad

  2. 按兩下 Main.storyboard 檔案,以開啟它以在Interface Builder 中編輯:

    Editing the main storyboard

  3. 將採用影像的 UI 元素拖曳到設計介面上(例如影像 工具列專案):

    Editing a toolbar item

  4. 在 [映射名稱] 下拉式清單中,選取您新增至 [資源] 資料夾的 [映射]:

    Selecting an image for a toolbar item

  5. 選取的影像會顯示在設計介面中:

    The image being displayed in the Toolbar editor

  6. 儲存變更並返回 Visual Studio for Mac 以與 Xcode 同步。

上述步驟適用於允許在屬性偵測器設定其影像屬性的任何UI元素。 同樣地,如果您已 包含圖像檔@2x 版本,它會自動在 Retina Display 型 Mac 上使用。

重要

如果影像無法在 [影像名稱] 下拉式清單中使用,請在 Xcode 中關閉 .storyboard 專案,然後從 Visual Studio for Mac 重新開啟它。 如果映像仍然無法使用,請確定其 [建置動作 ] 為 BundleResource ,且映射已新增至 [資源 ] 資料夾。

在 C# 程式代碼中使用影像

使用 Xamarin.Mac 應用程式中的 C# 程式代碼將影像載入記憶體時,映像會儲存在物件中 NSImage 。 如果映像檔已包含在 Xamarin.Mac 應用程式套件組合中(包含在資源中),請使用下列程式代碼來載入映像:

NSImage image = NSImage.ImageNamed("tags.png");

此程式代碼會使用 類別的靜態ImageNamed("...")方法,從 Resources 資料夾將指定的影像載入記憶體中,如果找不到影像,null則會傳NSImage回 。 就像介面產生器中指派的影像一樣,如果您已包含 影像檔案的@2x 版本,它會自動在 Retina Display 型 Mac 上使用。

若要在應用程式套件組合之外載入影像(從 Mac 檔案系統),請使用下列程式代碼:

NSImage image = new NSImage("/Users/KMullins/Documents/photo.jpg")

使用範本映像

根據 macOS 應用程式的設計,您可能需要自定義使用者介面內的圖示或影像,以符合色彩配置變更(例如,根據使用者喜好設定)。

若要達到此效果,請將影像資產的轉譯模式切換範本影像

Setting a template image

從 Xcode 的介面產生器中,將影像資產指派給 UI 控制項:

Selecting an image in Xcode's Interface Builder

或者選擇性地在程式代碼中設定映射來源:

MyIcon.Image = NSImage.ImageNamed ("MessageIcon");

將下列公用函式新增至檢視控制器:

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。 這類影像會變成靜態,且不會自動更新以考慮外觀或顯示密度變更。

藉由採用上述處理程式型機制,在 中裝載 時,會自動NSImageNSImageView重新轉譯動態條件。

最後,若要淡化範本影像,請針對影像呼叫此函式以著色:

MyIcon.Image = ImageTintedWithColor (MyIcon.Image, NSColor.Red);

搭配數據表檢視使用影像

若要在儲存格中包含 NSTableView影像,您必須變更資料表檢視 NSTableViewDelegate'sGetViewForItem 方法傳回資料的方式,以使用 NSTableCellView 而非一般 NSTextField。 例如:

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

這裡有幾行興趣。 首先,針對我們想要包含影像的數據行,我們會建立所需的大小和位置的新 ,我們也會建立新的 NSImageViewNSTextField ,並根據我們是否使用影像來放置其預設位置:

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

其次,我們需要在父 NSTableCellView系中包含新的影像檢視和文字欄位:

view.AddSubview (view.ImageView);
...

view.AddSubview (view.TextField);
...

最後,我們需要告訴文字欄位,它可以隨著數據表檢視儲存格縮小和成長:

view.TextField.AutoresizingMask = NSViewResizingMask.WidthSizable;

範例輸出︰

An example of displaying an image in an app

如需使用數據表檢視的詳細資訊,請參閱我們的 數據表檢視 檔。

搭配大綱檢視使用影像

若要在儲存格中包含 NSOutlineView影像,您必須變更大綱檢視 NSTableViewDelegate'sGetView 方法傳回資料的方式,以使用 NSTableCellView 而非一般 NSTextField。 例如:

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

這裡有幾行興趣。 首先,針對我們想要包含影像的數據行,我們會建立所需的大小和位置的新 ,我們也會建立新的 NSImageViewNSTextField ,並根據我們是否使用影像來放置其預設位置:

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

其次,我們需要在父 NSTableCellView系中包含新的影像檢視和文字欄位:

view.AddSubview (view.ImageView);
...

view.AddSubview (view.TextField);
...

最後,我們需要告訴文字欄位,它可以隨著數據表檢視儲存格縮小和成長:

view.TextField.AutoresizingMask = NSViewResizingMask.WidthSizable;

範例輸出︰

An example of an image being displayed in an Outline View

如需使用大綱檢視的詳細資訊,請參閱大綱 檢視 檔。

摘要

本文已詳細探討在 Xamarin.Mac 應用程式中使用影像和圖示。 我們看到了影像的不同類型和用法、如何在 Xcode 的介面產生器中使用影像和圖示,以及如何在 C# 程式代碼中使用影像和圖示。