- PVSM.RU - https://www.pvsm.ru -

iOS 10: Notification Content Extension

iOS 10: Notification Content Extension - 1

В этой статье речь пойдет о новой возможности в iOS 10 — Notification Content Extension. Это разновидность расширения, которая позволяет отображать пользователю собственный интерфейс при взаимодействии с уведомлением (remote или local). И отдельно коснемся того, что можно, а что нельзя делать в этом новом расширении — в том числе насколько оно гибко настраивается и конфигурируется.

Notification Content Extension позволяет взаимодействовать с приложением, формально не запуская его. Наибольшую продуктивность это расширение приносит, если от пользователя требуется одно или несколько простых действий в ответ на принятое уведомление. В последней beta-версии возможность взаимодействия пользователей с такими уведомлениями не ограничивается устройствами с поддержкой 3D Touch, на старых устройствах экран расширения покажется при жесте вниз на баннере уведомления.

Разберем в деталях этот новый механизм на тестовом проекте, с которым вы можете ознакомиться тут [1]:

  • Добавление target для extension в проект.
  • Проектирование и стилизация интерфейса.
  • Добавление действий и их обработка.
  • Ограничения по кастомизации интерфейса.

Добавление target для extension в проект

Для создания проекта нам понадобится Xcode 8 (в данный момент доступна beta-версия). Создаем новый проект знакомым нам способом, далее необходимо создать новый target для расширения через меню File — New — Target и выбрать Notification Content Extension.

iOS 10: Notification Content Extension - 2

Для расширения создается свой storyboard и view controller, который подчиняется протоколу UNNotificationContentExtension.

В нём присутствует один обязательный метод

func didReceive(_ notification: UNNotification)

который вызывается при получении нового уведомления в тот момент, когда расширение отображается на экране. В этом методе мы можем достать нужную информацию из уведомления и отобразить её. В рассматриваемом примере для простоты мы не будем его реализовывать.

Далее мы будем использовать другой необязательный метод

func didReceive(_ response: UNNotificationResponse, completionHandler completion: (UNNotificationContentExtensionResponseOption) -> Swift.Void)

чтобы обрабатывать нажатия кнопок (Notification Actions) и производить изменения в интерфейсе, открывать главное приложение.

Проектирование и стилизация интерфейса

Затем верстаем интерфейс как душе угодно в storyboard-файле, конфигурируем в унаследованных методах UIViewController.
iOS 10: Notification Content Extension - 3

Если мы не хотим, чтобы заголовок, подзаголовок, текст уведомления показывались в экране расширения, нужно указать ключ UNNotificationExtensionDefaultContentHidden со значением YES в plist.

При отображении экрана с расширением можно заметить проблему с его высотой. К сожалению, здесь нам недоступен механизм автоматического подсчета высоты. Исправить это можно следующим кодом, например, в методе viewDidLoad, предполагая высоту в 50%, достаточную для отображения всего контента:

let size = view.bounds.size
preferredContentSize = CGSize(width: size.width, height: size.width / 2)

В этом случае могут наблюдаться артефакты при показе – пользователь может заметить анимацию изменения размера высоты экрана.

iOS 10: Notification Content Extension - 4

К счастью, это можно исправить установкой коэффициента от максимальной высоты в plist (ключ UNNotificationExtensionInitialContentSizeRatio).

Окончательно plist выглядит следующим образом:

iOS 10: Notification Content Extension - 5

Добавление действий и их обработка

Для того, чтобы настроить пользовательские действия с уведомлением (Notification Actions), нужно их заранее подготовить – создать категорию уведомления и присвоить ей возможные действия над уведомлением:

let showMoreAction = UNNotificationAction(identifier: "showMore", title: "Подробнее", options: [])
let addBalanceAction = UNNotificationAction(identifier: "addBalance", title: "Пополнить на 500 ₽", options: [])
let myPlanAction = UNNotificationAction(identifier: "myPlan", title: "Мой тариф", options: [])
        
let balanceCategory = UNNotificationCategory(identifier: "balance", actions: [showMoreAction, addBalanceAction, myPlanAction], intentIdentifiers: [], options: [])
        
UNUserNotificationCenter.current().setNotificationCategories([balanceCategory])

Категория нужна для того, чтобы операционная система по её идентификатору показала возможные действия. Категория должна приходить в соответствующем поле в пуше:

{
   aps : { 
        alert : "Текст пуша",
        category: "balance"
   } 
}

Для того, чтобы эти действия можно было применить к локальному уведомлению, нужно заполнить соответствующее поле класса UNNotificationContent:

content.categoryIdentifier = "balance"

Дополнительно для показа конкретного расширения (если их несколько) нужно добавить в массив с ключом UNNotificationExtensionCategory идентификатор категории в plist. Если операционная система не находит ключ категории в доступных расширениях, уведомление обрабатывается стандартно (без показа какого-либо экрана расширения).

Нажатия на кнопки обрабатываются так:

switch response.actionIdentifier {
case "addBalance":
    addBalance()
    completion(.doNotDismiss)
case "myPlan":
    openMainApplication()
    completion(.dismiss)
case "showMore":
     openMainApplication()
     completion(.dismiss)
default:
     completion(.dismiss)
}

В этом методе мы можем выполнять любые (в том числе асинхронные) операции, но время на обработку действия ограничено, поэтому не стоит перегружать метод сложными и длительными процессами.

Completion должен быть вызван с элементом перечисления UNNotificationContentExtensionResponseOption:

  • dismiss – после выполнения уведомление скроется
  • doNotDismiss – после выполнения уведомление не скроется
  • dismissAndForwardAction – после выполнения уведомление скроется и действие перенаправится в основное приложение в метод func userNotificationCenter(_ center:, didReceive response:, withCompletionHandler completionHandler: ) делегата UNUserNotificationCenterDelegate

В нашем тестовом приложении мы асинхронно пополняем счет:

iOS 10: Notification Content Extension - 6

По другим действиям осуществляем переход в основное приложение привычным для расширения способом (предварительно зарегистрировав URL-схему приложения):

if let url = URL(string: "callProvider://") {
    extensionContext?.open(url, completionHandler: nil)
}

Ограничения по кастомизации интерфейса

В заключение перечислю ограничения по кастомизации расширения (этим знанием стоит поделиться с дизайнерами):

  1. Стили кнопок действий над уведомлением не кастомизируются. Нельзя поменять шрифт, выравнивание, добавить иконки к кнопкам и т.д.
  2. Фон экрана расширения не поддерживает (надеюсь, это касается только beta-версии) прозрачность. Все попытки установить фон в прозрачный и получить стандартный blur на фоне (как это работает в виджете), были неудачны.
  3. Белый заголовок экрана расширения (с названием приложения и иконкой “закрыть”) также полностью неконфигурируемый.

Подробнее об уведомлениях — в лекциях с WWDC 2016: Introduction to Notifications [2] и Advanced Notifications [3].

Благодарности за дизайн: Степанов Никита, Отрощенко Денис.

Автор: REDMADROBOT

Источник [4]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/ios-development/169339

Ссылки в тексте:

[1] тут: https://github.com/vani2/Call-Provider-Example

[2] Introduction to Notifications: https://developer.apple.com/videos/play/wwdc2016/707/

[3] Advanced Notifications: https://developer.apple.com/videos/play/wwdc2016/708/

[4] Источник: https://habrahabr.ru/post/306572/?utm_source=habrahabr&utm_medium=rss&utm_campaign=best