Site icon AppTractor

Изучаем WidgetKit: пишем виджет для Control Center в iOS 18

Иногда мне хотелось обновить виджеты фонарика и камеры на экране блокировки. Похоже, этот день настал. В iOS 18 мы можем сделать это благодаря виджетам управления (Control Widgets).

Я уже играл с виджетами, и когда виджеты управления были анонсированы на WWDC 2024, сессия, связанная с ними, была первой, которую я посмотрел, когда летел домой из SFO.

Архитектура настолько похожа на другие виджеты, что я за пятнадцать минут создал простой элемент управления для своего приложения Fusion, чтобы напрямую воспроизводить станцию в Apple Music.

В этой статье мы поговорим о контролах и о том, как начать работу с ними!

Что такое виджеты управления?

Виджеты управления появились в iOS 18 и iPadOS. Они позволяют нам добавлять быстрые действия из приложения в такие места, как Центр управления или нижние виджеты экрана блокировки. Это ярлыки для простых задач, таких как включение фонарика телефона, или действий вроде записи экрана.

Протокол ControlWidget

Начнем с протокола ControlWidget. Он похож на протокол виджета, с которым мы привыкли работать:

@available(iOS 18.0, *)
@MainActor protocol ControlWidget {
  
  @MainActor @preconcurrency init()
  
  associatedtype Body : ControlWidgetConfiguration

  @ControlWidgetConfigurationBuilder @MainActor @preconcurrency var body: Self.Body { get }
}
  1. У него есть свойство body. Здесь мы описываем, как выглядит и что делает виджет.
  2. Сам body возвращает конфигурацию ControlWidgetConfiguration.

ControlWidgetConfiguration

ControlWidgetConfiguration определяет тело элемента управления и настраивает его.

@available(iOS 18.0, *)
@MainActor public protocol ControlWidgetConfiguration {
  associatedtype Body : ControlWidgetConfiguration

  @ControlWidgetConfigurationBuilder @MainActor @preconcurrency var body: Self.Body { get }
}

Есть несколько вещей, которые мы можем с ним сделать:

ControlWidgetButton

ControlWidgetButton используется для создания кнопок в виджетах управления. Он предназначен для простых, немедленных действий и принимает внешний вид системы для согласованности.

@MainActor struct ControlWidgetButton<Label, ActionLabel>: ControlWidgetTemplate where Label: View, ActionLabel: View {
  @MainActor var body: some ControlWidgetTemplate { get }

  typealias Body = some ControlWidgetTemplate
}

extension ControlWidgetButton {
  @MainActor init<Action>(action: Action, @ViewBuilder label: @escaping () -> Label, @ViewBuilder actionLabel: @escaping (Bool) -> ActionLabel) where Action: AppIntent
}

Мы можем использовать его так:

ControlWidgetButton(action: SomeIntent()) {
  Label("Button Text", systemImage: "symbol.name")
}

Создание первого виджета управления

Давайте рассмотрим создание простого виджета управления с помощью ControlWidgetButton. В этом примере мы создадим кнопку для воспроизведения станции в Apple Music.

#if swift(>=6.0)
@available(iOS 18, *)
struct PlayDiscoveryStation: ControlWidget {
  var body: some ControlWidgetConfiguration {
    StaticControlConfiguration(kind: "com.rudrankriyam.fussion") {
      ControlWidgetButton(action: PlayDiscoveryStationIntent()) {
        Label("Play Discovery Station", systemImage: "radio.fill")
      }
    }
  }
}
#endif

В этом коде:

Обработка действия

Действие определяется в отдельном AppIntent:

import MusadoraKit

@available(iOS 18, *)
struct PlayDiscoveryStationIntent: AppIntent {
  static var title: LocalizedStringResource = "Play Apple Music Discovery Station"
  static var description = IntentDescription("Plays the Apple Music Discovery station in the control center.")

  init() {
  }

  func perform() async throws -> some IntentResult {
    try await WidgetMusicPlayer.playDiscoveryStation()
    return .result()
  }
}

Это намерение определяет, что происходит при нажатии на кнопку — в данном случае воспроизведение станции discovery с помощью класса WidgetMusicPlayer.

Функция perform() помечена как async, что позволяет ей ждать начала воспроизведения без блокировки. После начала воспроизведения она просто возвращает .result(), не возвращая ничего, так как действие запуска воспроизведения является предполагаемым результатом.

Интеграция виджетов управления с существующими пакетами виджетов

Если у нас уже есть WidgetBundle для приложения, мы обновим его, чтобы включить новые виджеты панели управления для iOS 18:

struct FussionWidgetsBundle: WidgetBundle {
  var body: some Widget {
    DiscoveryStationWidgets()
    PersonalStationWidgets()
    SongStationWidget()

    if #available(iOSApplicationExtension 18, *) {
      PlayDiscoveryStation()
    }
  }
}

Это позволяет приложению поддерживать как старые версии iOS с существующими виджетами, так и iOS 18 с новыми управляющими виджетами.

Вот как выглядит Control Widget в действии в Центре управления:

А вот как установить его на экран блокировки:

Что дальше

Когда мы освоим базовые виджеты, мы сможем изучить больше возможностей новых API. У нас есть ControlWidgetToggle для функций, которые имеют четкое состояние включения/выключения, или ControlValueProvider для актуальной информации.

Счастливого виджетирования!

Источник

Exit mobile version