Расширения iOS в Xamarin.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 10 и iOS 12 добавлены дополнительные точки расширения. Полную таблицу всех поддерживаемых типов можно найти в руководстве по программированию расширений приложений iOS.
Ограничения
Расширения имеют ряд ограничений, некоторые из которых являются универсальными для всех типов (например, ни один тип расширения не может получить доступ к камерам или микрофонам), а другие типы расширения могут иметь определенные ограничения на их использование (например, пользовательские клавиатуры нельзя использовать для безопасных полей записи данных, таких как для паролей).
Универсальные ограничения:
- Набор работоспособности и платформы пользовательского набора событий недоступны
- Расширения не могут использовать расширенные фоновые режимы
- Расширения не могут получить доступ к камерам или микрофонам устройства (хотя они могут получить доступ к существующим файлам мультимедиа)
- Расширения не могут получать данные air drop (хотя они могут передавать данные через Air Drop)
- UIActionSheet и UIAlertView недоступны. Расширения должны использовать UIAlertController
- Несколько членов UIApplication недоступны: UIApplication.SharedApplication, UIApplication.OpenUrl, UIApplication.BeginIgnoringInteractionEvents и UIApplication.EndIgnoringInteractionEvents
- IOS применяет ограничение на использование памяти в 16 МБ для расширений Сегодня.
- По умолчанию расширения клавиатуры не имеют доступа к сети. Это влияет на отладку на устройстве (ограничение не применяется в симуляторе), так как Xamarin.iOS требует сетевого доступа для отладки. Можно запросить сетевой доступ, задав
Requests Open Access
значение в файле Info.plistYes
проекта. Дополнительные сведения об ограничениях расширения клавиатуры см. в руководстве по пользовательской клавиатуре Apple.
Сведения об отдельных ограничениях см. в руководстве по программированию расширений приложений Apple.
Распространение, установка и запуск расширений
Расширения распределяются из приложения-контейнера, которое, в свою очередь, отправляется и распространяется через App Store. Расширения, распределенные с приложением, устанавливаются в этот момент, но пользователь должен включить каждое расширение явным образом. Различные типы расширений включены разными способами; для нескольких пользователей требуется перейти к приложению "Параметры" и включить их оттуда. В то время как другие пользователи включены на момент использования, например включение расширения общего доступа при отправке фотографии.
Приложение, в котором используется расширение (где пользователь сталкивается с точкой расширения) называется ведущим приложением, так как оно размещает расширение при выполнении. Приложение, устанавливающее расширение, является приложением-контейнером, так как это приложение, содержащее расширение при его установке.
Как правило, приложение-контейнер описывает расширение и выполняет пошаговое руководство пользователя по его включению.
Отладка и выпуск версий расширений
Ограничения памяти для запуска расширений приложений значительно ниже, чем ограничения памяти, применяемые к приложению переднего плана. Симуляторы под управлением iOS имеют меньше ограничений, применяемых к расширениям, и вы можете выполнить расширение без каких-либо проблем. Однако выполнение того же расширения на устройстве может привести к непредвиденным результатам, включая сбой расширения или агрессивное завершение системы. Поэтому перед доставкой убедитесь, что вы создаете и тестируете расширение на устройстве.
Необходимо убедиться, что следующие параметры применяются к проекту контейнера и всем расширениям, на которые ссылается ссылка:
- Создайте пакет приложения в конфигурации выпуска .
- В параметрах проекта сборки iOS задайте для параметра поведения компоновщика значение Link Framework SDK Only или Link All.
- В параметрах проекта отладки iOS снимите флажок Включить отладку и включить профилирование.
Жизненный цикл расширения
Расширение может быть таким же простым, как один UIViewController или более сложные расширения, которые представляют несколько экранов пользовательского интерфейса. Когда пользователь сталкивается с точками расширения (например, при совместном доступе к изображению), он сможет выбрать из расширений, зарегистрированных для этой точки расширения.
Если они выбирают одно из расширений вашего приложения, его UIViewController
экземпляр будет создан и начнется обычный жизненный цикл контроллера представления. Однако, в отличие от обычного приложения, которые приостановлены, но обычно не завершаются, когда пользователь завершает взаимодействие с ними, расширения загружаются, выполняются, а затем прерываются повторно.
Расширения могут взаимодействовать с приложениями узла через объект NSExtensionContext . Некоторые расширения имеют операции, получающие асинхронные обратные вызовы с результатами. Эти обратные вызовы будут выполняться в фоновых потоках, и расширение должно учитывать это; например, с помощью NSObject.InvokeOnMainThread , если они хотят обновить пользовательский интерфейс. Дополнительные сведения см. в разделе "Взаимодействие с приложением узла" ниже.
По умолчанию расширения и их контейнерные приложения не могут взаимодействовать, несмотря на то, что они установлены вместе. В некоторых случаях приложение-контейнер контейнера по сути является пустым контейнером "доставка", назначение которого выполняется после установки расширения. Однако если обстоятельства диктуют, приложение-контейнер и расширение могут совместно использовать ресурсы из общей области. Кроме того, расширение Today может запросить свое приложение-контейнер, чтобы открыть URL-адрес. Это поведение отображается в мини-приложении счетчика событий.
Создание расширения
Расширения (и их приложения-контейнеры) должны быть 64-разрядными двоичными файлами и создаваться с помощью унифицированных API Xamarin.iOS. При разработке расширения решения будут содержать по крайней мере два проекта: приложение-контейнер и один проект для каждого расширения, который предоставляет контейнер.
Требования к проекту приложения-контейнера
Приложение-контейнер, используемое для установки расширения, имеет следующие требования:
- Он должен поддерживать ссылку на проект расширения.
- Оно должно быть полным приложением (должно иметь возможность успешного запуска и запуска), даже если оно не делает ничего больше, чем предоставить способ установки расширения.
- Он должен иметь идентификатор пакета, который является основой для идентификатора пакета проекта расширения (дополнительные сведения см. в разделе ниже).
Требования к проекту расширения
Кроме того, проект расширения имеет следующие требования:
Он должен иметь идентификатор пакета, который начинается с идентификатора пакета приложения-контейнера. Например, если у приложения-контейнера есть идентификатор
com.myCompany.ContainerApp
пакета, идентификатор расширения может бытьcom.myCompany.ContainerApp.MyExtension
:Он должен определить ключ
NSExtensionPointIdentifier
с соответствующим значением (напримерcom.apple.widget-extension
, для мини-приложения Центра уведомлений сегодня ) в файлеInfo.plist
.Он также должен определить
NSExtensionMainStoryboard
ключ илиNSExtensionPrincipalClass
ключ в файлеInfo.plist
с соответствующим значением:NSExtensionMainStoryboard
Используйте ключ, чтобы указать имя раскадровки, представляющее основной пользовательский интерфейс расширения (минус.storyboard
). Например,Main
дляMain.storyboard
файла.NSExtensionPrincipalClass
Используйте ключ, чтобы указать класс, который будет инициализирован при запуске расширения. Значение должно соответствовать значению register объектаUIViewController
:
Определенные типы расширений могут иметь дополнительные требования. Например, основной класс расширения "Сегодня " или "Центр уведомлений" должен реализовать INCWidgetProviding.
Внимание
Если вы запускаете проект с помощью одного из шаблонов расширений, предоставляемых Visual Studio для Mac, большинство (если не все) эти требования будут предоставлены и выполнены автоматически шаблоном.
Пошаговое руководство
В следующем пошаговом руководстве вы создадите пример мини-приложения Today , который вычисляет день и количество оставшихся дней в году:
Создание решения
Чтобы создать необходимое решение, сделайте следующее:
Сначала создайте проект приложения с одним представлением iOS и нажмите кнопку "Далее":
Вызовите проект
TodayContainer
и нажмите кнопку "Далее ":Проверьте имя проекта и имя решения и нажмите кнопку "Создать", чтобы создать решение:
Затем в Обозреватель решений щелкните правой кнопкой мыши решение и добавьте новый проект расширения iOS из шаблона расширения today:
Вызовите проект
DaysRemaining
и нажмите кнопку "Далее ":Просмотрите проект и нажмите кнопку "Создать ", чтобы создать ее:
Теперь результирующее решение должно иметь два проекта, как показано здесь:
Создание пользовательского интерфейса расширения
Затем вам потребуется разработать интерфейс для мини-приложения Today . Это можно сделать с помощью раскадровки или создания пользовательского интерфейса в коде. Оба метода подробно описаны ниже.
Использование раскадровки
Чтобы создать пользовательский интерфейс с раскадровкой, сделайте следующее:
В Обозреватель решений дважды щелкните файл проекта
Main.storyboard
расширения, чтобы открыть его для редактирования:Выберите метку, которая была автоматически добавлена в пользовательский интерфейс по шаблону, и присвойте ему имя
TodayMessage
на вкладке "Мини-приложение" обозревателя свойств:Сохраните изменения в раскадровке.
Использование кода
Чтобы создать пользовательский интерфейс в коде, сделайте следующее:
В Обозреватель решений выберите проект DaysRemaining, добавьте новый класс и вызовите его
CodeBasedViewController
:Снова в Обозреватель решений дважды щелкните файл расширения
Info.plist
, чтобы открыть его для редактирования:Выберите исходное представление (в нижней части экрана) и откройте
NSExtension
узел:NSExtensionMainStoryboard
Удалите ключ и добавьтеNSExtensionPrincipalClass
значениеCodeBasedViewController
:Сохранение изменений.
Затем измените 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.cs
файл или CodeBasedViewController.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...
комментарий новым кодом, приведенным выше. После вызова базовой реализации (и вставки метки для версии на основе кода) этот код выполняет простое вычисление, чтобы получить день года и сколько дней осталось. Затем отображается сообщение в метке (TodayMessage
), созданной в конструкторе пользовательского интерфейса.
Обратите внимание, насколько похож этот процесс на обычный процесс написания приложения. Расширение UIViewController
имеет тот же жизненный цикл, что и контроллер представления в приложении, за исключением того, что расширения не имеют фоновых режимов и не приостановлены, когда пользователь завершит работу с ними. Вместо этого расширения многократно инициализируются и удаляются по мере необходимости.
Создание пользовательского интерфейса приложения-контейнера
В этом пошаговом руководстве приложение-контейнер просто используется в качестве метода для отправки и установки расширения и не предоставляет собственных функций. Измените файл TodayContainer Main.storyboard
и добавьте некоторый текст, определяющий функцию расширения и как его установить:
Сохраните изменения в раскадровке.
Тестирование расширения
Чтобы протестировать расширение в симуляторе iOS, запустите приложение TodayContainer . Откроется основное представление контейнера:
Затем нажмите кнопку "Главная " в симуляторе, проводите пальцем вниз в верхней части экрана, чтобы открыть центр уведомлений, перейдите на вкладку "Сегодня " и нажмите кнопку " Изменить ".
Добавьте расширение DaysRemaining в представление "Сегодня" и нажмите кнопку "Готово":
Новое мини-приложение будет добавлено в представление "Сегодня" , и результаты будут отображаться:
Взаимодействие с ведущим приложением
Пример созданного выше расширения today не взаимодействует с его ведущим приложением ( на экране "Сегодня "). Если бы он сделал, он будет использовать свойство ExtensionContext для TodayViewController
классов или CodeBasedViewController
классов.
Для расширений, которые будут получать данные из своих ведущих приложений, данные хранятся в виде массива объектов NSExtensionItem, хранящихся в свойстве InputItems расширенияContext расширенияUIViewController
.
Другие расширения, такие как расширения редактирования фотографий, могут различаться между пользователем, выполняющим или отменяющим использование. Это будет сигнализировать обратно в хост-приложение с помощью методов CompleteRequest и CancelRequest свойства ExtensionContext.
Дополнительные сведения см. в руководстве по программированию расширений приложений Apple.
Взаимодействие с родительским приложением
Группы приложений предоставляют различным приложениям (или одному приложению и его расширениям) общее место хранения файлов. Группы приложений можно использовать для следующих данных:
- параметры Apple Watch.
- Общие NSUserDefaults.
- Общие файлы.
Дополнительные сведения см. в разделе "Группы приложений" в документации по работе с возможностями .
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");
}
});
Дополнительные сведения см. в разделе "Группы приложений" в документации по работе с возможностями .
Меры предосторожности и рекомендации
Расширения имеют значительно меньше памяти, чем приложения. Ожидается, что они выполняются быстро и с минимальным вторжением к пользователю и приложению, в котором они размещаются. Однако расширение также должно предоставлять отличительную, полезную функцию для используемого приложения с фирменной символикой пользовательского интерфейса, который позволяет пользователю определить разработчик расширения или приложение контейнера, к которому они относятся.
Учитывая эти жесткие требования, следует развертывать только расширения, которые были тщательно протестированы и оптимизированы для производительности и потребления памяти.
Итоги
В этом документе рассматриваются расширения, их тип, тип точек расширения и известные ограничения, введенные для расширения iOS. Он обсудил создание, распространение, установку и запуск расширений и жизненный цикл расширения. В нем представлено пошаговое руководство по созданию простого мини-приложения Today с двумя способами создания пользовательского интерфейса мини-приложения с помощью раскадровки или кода. В нем показано, как протестировать расширение в симуляторе iOS. Наконец, он кратко обсудил взаимодействие с приложением узла и несколько мер предосторожности и рекомендации, которые следует принять при разработке расширения.