共用方式為


Xamarin.iOS 中的 iOS 擴充功能

在 iOS 影片中建立擴充功能

延伸模組如 iOS 8 中介紹,是 UIViewControllers iOS 在標準內容中呈現的特殊功能,例如通知中心,使用者要求執行特殊輸入或其他內容,例如編輯延伸模組可以提供特殊效果篩選的相片等自定義鍵盤類型。

所有擴充功能都會與容器應用程式一起安裝(使用64位整合API撰寫的兩個元素),並從主機應用程式中的特定擴充點啟動。 而且,由於它們將作為現有系統功能的補充,因此它們必須是高效能、精簡且健全的。

擴充點

類型 描述 擴充點 主應用程式
動作 特定媒體類型的特製化編輯器或查看器 com.apple.ui-services 任意
檔提供者 允許應用程式使用遠端檔案存放區 com.apple.fileprovider-ui 使用 UIDocumentPickerViewController 的應用程式
鍵盤 替代鍵盤 com.apple.keyboard-service 任意
相片編輯 相片操作和編輯 com.apple.photo-editing Photos.app 編輯器
共用 與社交網路、傳訊服務等共享數據。 com.apple.share-services 任意
Today 出現在 [今日] 畫面或通知中心的 [小工具] com.apple.widget-extensions 今日和通知中心

iOS 10iOS 12 已新增其他擴充點。 您可以在 iOS 應用程式延伸模組程式設計指南中找到所有支援類型的完整資料表。

限制

延伸模組有一些限制,其中有些是適用於所有類型的(例如,沒有一種類型的延伸模組可以存取相機或麥克風),而其他類型的延伸模組可能會對其使用有特定限制(例如,自定義鍵盤不能用於安全的數據輸入字段,例如密碼)。

通用限制如下:

如需個別限制,請參閱Apple的應用程式 延伸模組程序設計指南

散發、安裝和執行擴充功能

擴充功能會從容器應用程式內散發,接著會透過App Store提交和散發。 隨應用程式一起散發的擴充功能會安裝於該時間點,但使用者必須明確啟用每個擴充功能。 不同類型的擴充功能會以不同的方式啟用;有數個要求使用者流覽至 設定 應用程式,並從該處啟用它們。 雖然其他專案會在使用時啟用,例如在傳送相片時啟用共用延伸模組。

使用擴充功能的應用程式(其中使用者遇到擴充點)稱為 「主機」應用程式,因為它是在執行擴充功能時裝載擴充功能的應用程式。 安裝擴充功能的應用程式是 容器應用程式,因為它是安裝擴充功能時包含擴充功能的應用程式。

一般而言,容器應用程式會描述擴充功能,並逐步引導使用者完成啟用它的程式。

擴充功能偵錯和發行版本

執行中應用程式延伸模組的記憶體限制明顯低於套用至前景應用程式的記憶體限制。 執行 iOS 的模擬器對延伸模組的限制較少,而且您可以執行擴充功能,而沒有任何問題。 不過,在裝置上執行相同的擴充功能可能會導致非預期的結果,包括擴充功能當機或系統主動終止。 因此,請務必先在裝置上建置及測試擴充功能,再寄送。

您應該確定下列設定會套用至容器專案和所有參考的延伸模組:

  1. 在發行組態中建置應用程式套件。
  2. iOS 建置項目設定中,將 [鏈接器行為] 選項設定為 [僅鏈接架構 SDK] 或 [全部連結]。
  3. 在 iOS 偵錯項目設定中,取消核取 [啟用偵錯] 和 [啟用程式代碼剖析] 選項。

延伸模組生命週期

擴充功能可以像呈現多個UI畫面的單 一UIViewController 或更複雜的延伸模組一樣簡單。 當使用者遇到 擴充點 時(例如共用影像時),他們將有機會從為該延伸點註冊的延伸模塊中選擇。

如果他們選擇其中一個應用程式的延伸模組,則會 UIViewController 將其具現化,並開始一般的檢視控制器生命週期。 不過,與一般應用程式不同,一般應用程式在使用者完成與其互動時會暫停,但通常不會終止,擴充功能會載入、執行,然後重複終止。

延伸模組可以透過 NSExtensionContext 物件與其主機應用程式通訊。 某些擴充功能具有可接收結果異步回呼的作業。 這些回呼將會在背景線程上執行,而延伸模組必須將此納入考慮;例如,如果您想要更新使用者介面,請使用 NSObject.InvokeOnMainThread 。 如需詳細資訊,請參閱下方的 <與主機應用程式 通訊>一節。

根據預設,延伸模組及其容器應用程式無法通訊,儘管一起安裝。 在某些情況下,容器應用程式基本上是空的「出貨」容器,其用途會在安裝擴充功能之後提供。 不過,如果情況規定,容器應用程式和延伸模組可能會從通用區域共享資源。 此外, Today Extension 可能會要求其容器應用程式開啟 URL。 此行為會顯示在事件倒數小工具

建立延伸模組

擴充功能(及其容器應用程式)必須是64位二進位檔,並使用 Xamarin.iOS 整合 API 來建置。 開發擴充功能時,您的解決方案至少會包含兩個專案:容器應用程式,以及容器所提供的每個延伸模組一個專案。

容器應用程式專案需求

用來安裝擴充功能的容器應用程式具有下列需求:

  • 它必須維護延伸模組項目的參考。
  • 它必須是完整的應用程式(必須能夠成功啟動並執行),即使它只做不到提供安裝延伸模組的方式。
  • 它必須有套件組合標識碼,這是延伸模組專案套件組合標識符的基礎(如需詳細資訊,請參閱下一節)。

延伸模組專案需求

此外,延伸模組的專案也有下列需求:

  • 它必須具有以容器應用程式套件組合標識符開頭的套件組合標識碼。 例如,如果容器應用程式的套件組合識別碼 com.myCompany.ContainerApp為 ,則延伸模組的識別碼可能是 com.myCompany.ContainerApp.MyExtension

    套件組合標識碼

  • 它必須定義索引鍵 NSExtensionPointIdentifier,其檔案中Info.plist具有適當的值(例如com.apple.widget-extension今日通知中心小工具)。

  • 它也必須使用適當的值,定義NSExtensionMainStoryboardInfo.plist檔案中的索引鍵或NSExtensionPrincipalClass索引鍵:

    • NSExtensionMainStoryboard使用 索引鍵來指定分鏡腳本的名稱,此腳本會顯示延伸模組的主要UI(減號 .storyboard)。 例如, Main 針對 Main.storyboard 檔案。
    • NSExtensionPrincipalClass使用 索引鍵來指定在啟動擴充功能時將初始化的類別。 此值必須符合UIViewController的 Register 值:

    主體類別註冊

特定類型的延伸模組可能會有其他需求。 例如,Today 或 Notification Center Extension 的主體類別必須實作 INCWidgetProviding。

重要

如果您使用 Visual Studio for Mac 提供的延伸模組範本來啟動專案,範本會自動提供這些需求,並為您自動符合這些需求。

逐步解說

在下列逐步解說中,您將建立一個 範例 Today 小工具,以計算年度剩餘的天數和天數:

今天小工具範例,可計算年度剩餘的天數和天數

建立解決方案

若要建立必要的解決方案,請執行下列動作:

  1. 首先,建立新的 iOS 單 一檢視應用程式 專案,然後按兩下 一步 : 按鈕:

    首先,建立新的 iOS、單一檢視應用程式專案,然後按兩下一步] 按鈕

  2. 呼叫項目 TodayContainer ,然後按兩下一 步: 按鈕:

    呼叫 TodayContainer 專案,然後按兩下一步] 按鈕

  3. 確認 [項目名稱] 和 [SolutionName],然後按兩下 [建立] 按鈕以建立方案:

    確認 [項目名稱] 和 [SolutionName],然後按兩下 [建立] 按鈕以建立方案

  4. 接下來,在 [方案總管] 中,以滑鼠右鍵按兩下 [解決方案],然後從 [今日擴充功能] 範本新增 iOS 擴充功能專案:

    接下來,在 方案總管 中,以滑鼠右鍵按兩下 [解決方案],然後從 [今日擴充功能] 範本新增 iOS 擴充功能專案

  5. 呼叫項目 DaysRemaining ,然後按兩下一 步: 按鈕:

    呼叫專案 DaysRemaining,然後按兩下一步[ 下一步] 按鈕

  6. 檢閱專案,然後按兩下 [ 建立] 按鈕來建立它:

    檢閱專案,然後按兩下 [建立] 按鈕加以建立

產生的解決方案現在應該有兩個專案,如下所示:

產生的解決方案現在應該有兩個專案,如下所示

建立擴充功能用戶介面

接下來,您必須設計 Today 小工具的介面。 這可以使用分鏡腳本或在程序代碼中建立UI來完成。 以下將詳細說明這兩種方法。

使用分鏡腳本

若要使用分鏡腳本建置 UI,請執行下列動作:

  1. 方案總管 中,按兩下 [延伸模組] 項目的Main.storyboard檔案以開啟它以進行編輯:

    按兩下 Extension 專案 Main.storyboard 檔案以開啟它以進行編輯

  2. 選取依範本自動新增至 UI 的標籤,並在 [屬性總管] 的 [小工具] 索引標籤中提供 [名稱TodayMessage]:

    選取依範本自動新增至 UI 的標籤,並在 [屬性總管] 的 [小工具] 索引標籤中為它命名 TodayMessage

  3. 將變更儲存至分鏡腳本。

使用程序代碼

若要在程式代碼中建置 UI,請執行下列動作:

  1. 方案總管 中,選取 DaysRemaining 專案、新增類別並加以呼叫CodeBasedViewController

    Aelect DaysRemaining 專案、新增類別並呼叫 CodeBasedViewController

  2. 同樣地,在 方案總管 中,按兩下 [延伸模組] 的Info.plist檔案以開啟它以進行編輯:

    按兩下 [延伸模組 Info.plist] 檔案以開啟它以進行編輯

  3. 選取 [ 來源檢視 ] [從畫面底部] 並開啟 NSExtension 節點:

    從畫面底部選取 [來源檢視],然後開啟 NSExtension 節點

  4. 移除索引鍵,NSExtensionMainStoryboard並使用 值CodeBasedViewController新增 NSExtensionPrincipalClass

    拿掉 NSExtensionMainStoryboard 機碼,並使用 CodeBasedViewController 值新增 NSExtensionPrincipalClass

  5. 儲存您的變更。

接下來,編輯 CodeBasedViewController.cs 檔案,使其看起來如下:

using System;
using Foundation;
using UIKit;
using NotificationCenter;
using CoreGraphics;

namespace DaysRemaining
{
  [Register("CodeBasedViewController")]
  public class CodeBasedViewController : UIViewController, INCWidgetProviding
  {
    public CodeBasedViewController ()
    {
    }

    public override void ViewDidLoad ()
    {
      base.ViewDidLoad ();

      // Add label to view
      var TodayMessage = new UILabel (new CGRect (0, 0, View.Frame.Width, View.Frame.Height)) {
        TextAlignment = UITextAlignment.Center
      };

      View.AddSubview (TodayMessage);

      // Insert code to power extension here...

    }
  }
}

請注意,符合 [Register("CodeBasedViewController")] 您為 NSExtensionPrincipalClass 上述指定的值。

撰寫延伸模組的程序代碼

建立使用者介面之後,開啟 TodayViewController.csCodeBasedViewController.cs 檔案(根據上述用來建立使用者介面的方法),變更 ViewDidLoad 方法,使其看起來如下:

public override void ViewDidLoad ()
{
  base.ViewDidLoad ();

  // Calculate the values
  var dayOfYear = DateTime.Now.DayOfYear;
  var leapYearExtra = DateTime.IsLeapYear (DateTime.Now.Year) ? 1 : 0;
  var daysRemaining = 365 + leapYearExtra - dayOfYear;

  // Display the message
  if (daysRemaining == 1) {
    TodayMessage.Text = String.Format ("Today is day {0}. There is one day remaining in the year.", dayOfYear);
  } else {
    TodayMessage.Text = String.Format ("Today is day {0}. There are {1} days remaining in the year.", dayOfYear, daysRemaining);
  }
}

如果使用以程式代碼為基礎的使用者介面方法,請將 // Insert code to power extension here... 批註取代為上述新程序代碼。 呼叫基底實作(並插入以程式代碼為基礎的版本標籤)之後,此程式代碼會執行簡單的計算,以取得年份的日期和剩餘天數。 然後,它會在UI設計中建立的標籤 (TodayMessage) 中顯示訊息。

請注意,此程式與撰寫應用程式的一般程式相似。 延伸模組的生命週期與應用程式中的 UIViewController 檢視控制器相同,但延伸模組沒有背景模式,且在使用者完成使用時不會暫停。 相反地,擴充功能會視需要重複初始化和取消配置。

建立容器應用程式用戶介面

在本逐步解說中,容器應用程式只是用來作為寄送並安裝延伸模組的方法,而且不提供自己的功能。 編輯 TodayContainer 的 Main.storyboard 檔案,並新增一些定義 Extension 函式的文字,以及如何安裝它:

編輯 TodayContainers Main.storyboard 檔案,並新增定義 Extensions 函式的一些文字,以及如何安裝它

將變更儲存至分鏡腳本。

測試擴充功能

若要在 iOS 模擬器中測試延伸模組,請執行 TodayContainer 應用程式。 容器的主要檢視將會顯示:

容器主要檢視隨即顯示

接下來,按下模擬器中的 [首頁] 按鈕,從畫面頂端向下撥動以開啟通知中心,選取 [今日] 索引卷標,然後按兩下 [編輯] 按鈕:

按兩下模擬器中的 [首頁] 按鈕,從畫面頂端向下撥動以開啟通知中心,選取 [今日] 索引卷標,然後按兩下 [編輯] 按鈕

將 DaysRemaining 擴充功能新增至 [今日] 檢視,然後按兩下 [完成] 按鈕:

將 DaysRemaining 擴充功能新增至 [今日] 檢視,然後按兩下 [完成] 按鈕

新的小工具將會新增至 [ 今日 ] 檢視,並顯示結果:

新的小工具將會新增至 [今日] 檢視,並顯示結果

與主機應用程式通訊

您上述建立的今日延伸模組範例不會與其主機應用程式通訊(今日畫面)。 如果這樣做,它會使用 或 類別的 TodayViewController ExtensionContextCodeBasedViewController 屬性。

針對從主應用程式接收數據的延伸模組,數據的格式為儲存在 ExtensionContext 之 ExtensionContext UIViewControllerInputItems 屬性中的 NSExtensionItem 物件數位

其他延伸模組,例如相片編輯延伸模組,可能會區分使用者完成或取消使用量。 這會透過 ExtensionContext 屬性的 CompleteRequest 和 CancelRequest 方法傳回主機應用程式。

如需詳細資訊,請參閱Apple的應用程式 延伸模組程序設計指南

與父應用程式通訊

「應用程式群組」可讓不同的應用程式 (或應用程式及其擴充功能) 存取共用檔案儲存體位置。 「應用程式群組」可用於資料下列資料:

如需詳細資訊,請參閱使用功能檔的應用程式群組一節。

MobileCoreServices

使用延伸模組時,請使用統一類型標識碼 (UTI) 來建立及操作應用程式、其他應用程式和/或服務之間交換的數據。

MobileCoreServices.UTType靜態類別會定義下列與 Apple kUTType... 定義相關的協助程式屬性:

  • kUTTypeAlembic - Alembic
  • kUTTypeAliasFile - AliasFile
  • kUTTypeAliasRecord - AliasRecord
  • kUTTypeAppleICNS - AppleICNS
  • kUTTypeAppleProtectedMPEG4Audio - AppleProtectedMPEG4Audio
  • kUTTypeAppleProtectedMPEG4Video - AppleProtectedMPEG4Video
  • kUTTypeAppleScript - AppleScript
  • kUTTypeApplication - Application
  • kUTTypeApplicationBundle - ApplicationBundle
  • kUTTypeApplicationFile - ApplicationFile
  • kUTTypeArchive - Archive
  • kUTTypeAssemblyLanguageSource - AssemblyLanguageSource
  • kUTTypeAudio - Audio
  • kUTTypeAudioInterchangeFileFormat - AudioInterchangeFileFormat
  • kUTTypeAudiovisualContent - AudiovisualContent
  • kUTTypeAVIMovie - AVIMovie
  • kUTTypeBinaryPropertyList - BinaryPropertyList
  • kUTTypeBMP - BMP
  • kUTTypeBookmark - Bookmark
  • kUTTypeBundle - Bundle
  • kUTTypeBzip2Archive - Bzip2Archive
  • kUTTypeCalendarEvent - CalendarEvent
  • kUTTypeCHeader - CHeader
  • kUTTypeCommaSeparatedText - CommaSeparatedText
  • kUTTypeCompositeContent - CompositeContent
  • kUTTypeConformsToKey - ConformsToKey
  • kUTTypeContact - Contact
  • kUTTypeContent - Content
  • kUTTypeCPlusPlusHeader - CPlusPlusHeader
  • kUTTypeCPlusPlusSource - CPlusPlusSource
  • kUTTypeCSource - CSource
  • kUTTypeData - Database
  • kUTTypeDelimitedText - DelimitedText
  • kUTTypeDescriptionKey - DescriptionKey
  • kUTTypeDirectory - Directory
  • kUTTypeDiskImage - DiskImage
  • kUTTypeElectronicPublication - ElectronicPublication
  • kUTTypeEmailMessage - EmailMessage
  • kUTTypeExecutable - Executable
  • kUTExportedTypeDeclarationsKey - ExportedTypeDeclarationsKey
  • kUTTypeFileURL - FileURL
  • kUTTypeFlatRTFD - FlatRTFD
  • kUTTypeFolder - Folder
  • kUTTypeFont - Font
  • kUTTypeFramework - Framework
  • kUTTypeGIF - GIF
  • kUTTypeGNUZipArchive - GNUZipArchive
  • kUTTypeHTML - HTML
  • kUTTypeICO - ICO
  • kUTTypeIconFileKey - IconFileKey
  • kUTTypeIdentifierKey - IdentifierKey
  • kUTTypeImage - Image
  • kUTImportedTypeDeclarationsKey - ImportedTypeDeclarationsKey
  • kUTTypeInkText - InkText
  • kUTTypeInternetLocation - InternetLocation
  • kUTTypeItem - Item
  • kUTTypeJavaArchive - JavaArchive
  • kUTTypeJavaClass - JavaClass
  • kUTTypeJavaScript - JavaScript
  • kUTTypeJavaSource - JavaSource
  • kUTTypeJPEG - JPEG
  • kUTTypeJPEG2000 - JPEG2000
  • kUTTypeJSON - JSON
  • kUTType3dObject - k3dObject
  • kUTTypeLivePhoto - LivePhoto
  • kUTTypeLog - Log
  • kUTTypeM3UPlaylist - M3UPlaylist
  • kUTTypeMessage - Message
  • kUTTypeMIDIAudio - MIDIAudio
  • kUTTypeMountPoint - MountPoint
  • kUTTypeMovie - Movie
  • kUTTypeMP3 - MP3
  • kUTTypeMPEG - MPEG
  • kUTTypeMPEG2TransportStream - MPEG2TransportStream
  • kUTTypeMPEG2Video - MPEG2Video
  • kUTTypeMPEG4 - MPEG4
  • kUTTypeMPEG4Audio - MPEG4Audio
  • kUTTypeObjectiveCPlusPlusSource - ObjectiveCPlusPlusSource
  • kUTTypeObjectiveCSource - ObjectiveCSource
  • kUTTypeOSAScript - OSAScript
  • kUTTypeOSAScriptBundle - OSAScriptBundle
  • kUTTypePackage - Package
  • kUTTypePDF - PDF
  • kUTTypePerlScript - PerlScript
  • kUTTypePHPScript - PHPScript
  • kUTTypePICT - PICT
  • kUTTypePKCS12 - PKCS12
  • kUTTypePlainText - PlainText
  • kUTTypePlaylist - Playlist
  • kUTTypePluginBundle - PluginBundle
  • kUTTypePNG - PNG
  • kUTTypePolygon - Polygon
  • kUTTypePresentation - Presentation
  • kUTTypePropertyList - PropertyList
  • kUTTypePythonScript - PythonScript
  • kUTTypeQuickLookGenerator - QuickLookGenerator
  • kUTTypeQuickTimeImage - QuickTimeImage
  • kUTTypeQuickTimeMovie - QuickTimeMovie
  • kUTTypeRawImage - RawImage
  • kUTTypeReferenceURLKey - ReferenceURLKey
  • kUTTypeResolvable - Resolvable
  • kUTTypeRTF - RTF
  • kUTTypeRTFD - RTFD
  • kUTTypeRubyScript - RubyScript
  • kUTTypeScalableVectorGraphics - ScalableVectorGraphics
  • kUTTypeScript - Script
  • kUTTypeShellScript - ShellScript
  • kUTTypeSourceCode - SourceCode
  • kUTTypeSpotlightImporter - SpotlightImporter
  • kUTTypeSpreadsheet - Spreadsheet
  • kUTTypeStereolithography - Stereolithography
  • kUTTypeSwiftSource - SwiftSource
  • kUTTypeSymLink - SymLink
  • kUTTypeSystemPreferencesPane - SystemPreferencesPane
  • kUTTypeTabSeparatedText - TabSeparatedText
  • kUTTagClassFilenameExtension - TagClassFilenameExtension
  • kUTTagClassMIMEType - TagClassMIMEType
  • kUTTypeTagSpecificationKey - TagSpecificationKey
  • kUTTypeText - Text
  • kUTType3DContent - ThreeDContent
  • kUTTypeTIFF - TIFF
  • kUTTypeToDoItem - ToDoItem
  • kUTTypeTXNTextAndMultimediaData - TXNTextAndMultimediaData
  • kUTTypeUniversalSceneDescription - UniversalSceneDescription
  • kUTTypeUnixExecutable - UnixExecutable
  • kUTTypeURL - URL
  • kUTTypeURLBookmarkData - URLBookmarkData
  • kUTTypeUTF16ExternalPlainText - UTF16ExternalPlainText
  • kUTTypeUTF16PlainText - UTF16PlainText
  • kUTTypeUTF8PlainText - UTF8PlainText
  • kUTTypeUTF8TabSeparatedText - UTF8TabSeparatedText
  • kUTTypeVCard - VCard
  • kUTTypeVersionKey - VersionKey
  • kUTTypeVideo - Video
  • kUTTypeVolume - Volume
  • kUTTypeWaveformAudio - WaveformAudio
  • kUTTypeWebArchive - WebArchive
  • kUTTypeWindowsExecutable - WindowsExecutable
  • kUTTypeX509Certificate - X509Certificate
  • kUTTypeXML - XML
  • kUTTypeXMLPropertyList - XMLPropertyList
  • kUTTypeXPCService - XPCService
  • kUTTypeZipArchive - ZipArchive

請參閱下列範例:

using MobileCoreServices;
...

NSItemProvider itemProvider = new NSItemProvider ();
itemProvider.LoadItem(UTType.PropertyList ,null, (item, err) => {
    if (err == null) {
        NSDictionary results = (NSDictionary )item;
        NSString baseURI =
results.ObjectForKey("NSExtensionJavaScriptPreprocessingResultsKey");
    }
});

如需詳細資訊,請參閱使用功能檔的應用程式群組一節。

預防措施和考慮

延伸模組的可用記憶體比應用程式少得多。 它們預期會快速執行,且對用戶和他們裝載的應用程式進行最少入侵。 不過,延伸模組也應該為取用應用程式提供獨特的實用函式,其具有品牌UI,可讓用戶識別其所屬的擴充功能開發人員或容器應用程式。

鑒於這些嚴格的需求,您只應該部署經過徹底測試和優化以達到效能和記憶體耗用量的擴充功能。

摘要

本文件涵蓋擴充功能、擴充點類型,以及iOS對延伸模組所加之已知限制的類型。 它討論如何建立、散發、安裝及執行擴充功能和延伸模組生命週期。 它提供建立簡單 Today 小工具的逐步解說,其中顯示使用分鏡腳本或程式代碼建立小工具 UI 的兩種方式。 它示範如何在 iOS 模擬器中測試延伸模組。 最後,它會簡短討論與主機應用程式通訊,以及開發延伸模組時應採取的一些預防措施和考慮。