Как Создать Виджет Экрана Блокировки в iOS?

20 декабря 2022 г. | Разработка | Ли Ках Сенг

В iOS 16 Apple провела масштабную модернизацию Экран Блокировки. Одной из самых ожидаемых функций, которая появилась вместе с обновлением, - виджеты Экрана Блокировки. Как следует из названия, виджеты Экрана Блокировки — это виджеты, отображающие легко просматриваемый контент, который постоянно виден на экране блокировки iPhone и iPad.

Поскольку и виджеты Главного Экрана, и виджеты Экрана Блокировки работают на WidgetKit, способ создания виджета Экран Блокировки очень похож на то, как мы создаем виджеты Главного Экрана. Поэтому в этой статье я не буду показывать вам, как настроить и создать виджет с нуля, как это было описано в моей предыдущей статье.

Вместо этого я сосредоточусь на том, как обновить код существующих виджетов Главного Экрана для поддержки виджетов Экрана Блокировки.

С учетом всего сказанного, давайте начнем!

Краткое Резюме

В демонстрационных целях давайте обновим View Size Widget, который я создал в предыдущей статье. Вкратце напомню, что View Size Widget — это статический виджет Главного Экрана, который отображает размер вью самого виджета. Вот как это выглядит:

View Size Widget

Вот полная реализация View Size Widget:

import WidgetKit import SwiftUI // MARK: - The Timeline Entry struct ViewSizeEntry: TimelineEntry {     let date: Date     let providerInfo: String } // MARK: - The Widget View struct ViewSizeWidgetView : View {         let entry: ViewSizeEntry     var body: some View {         GeometryReader { geometry in             VStack {                                  // Show view size                 Text("\(Int(geometry.size.width)) x \(Int(geometry.size.height))")                     .font(.system(.title2, weight: .bold))                                  // Show provider info                 Text(entry.providerInfo)                     .font(.footnote)             }             .frame(maxWidth: .infinity, maxHeight: .infinity)             .background(Color.green)         }     } } // MARK: - The Timeline Provider struct ViewSizeTimelineProvider: TimelineProvider {          typealias Entry = ViewSizeEntry          func placeholder(in context: Context) -> Entry {         // This data will be masked         return ViewSizeEntry(date: Date(), providerInfo: "placeholder")     }     func getSnapshot(in context: Context, completion: @escaping (Entry) -> ()) {         let entry = ViewSizeEntry(date: Date(), providerInfo: "snapshot")         completion(entry)     }     func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {         let entry = ViewSizeEntry(date: Date(), providerInfo: "timeline")         let timeline = Timeline(entries: [entry], policy: .never)         completion(timeline)     } } // MARK: - The Widget Configuration @main struct ViewSizeWidget: Widget {          var body: some WidgetConfiguration {         StaticConfiguration(             kind: "com.SwiftSenpaiDemo.ViewSizeWidget",             provider: ViewSizeTimelineProvider()         ) { entry in             ViewSizeWidgetView(entry: entry)         }         .configurationDisplayName("View Size Widget")         .description("This is a demo widget.")         .supportedFamilies([             .systemSmall,             .systemMedium,             .systemLarge,         ])     } }

Если приведенный выше код не имеет для вас никакого смысла, прежде чем продолжить, я настоятельно рекомендую вам сначала прочитать мою статью под названием «Начало работы с WidgetKit».

Добавление Виджетов Экрана Блокировки в Ваши Приложения

Добавление Поддерживаемых Семейств Виджетов

В iOS 16 Apple представила 3 новых семейства виджетов, которые представляют 3 разных типа виджетов Экрана Блокировки, а именно: accessoryCircular, accessoryRectangular и accessorInline.

Новые семейства виджетов для виджетов Экрана Блокировки

Давайте продолжим и сделаем эти 3 новых семейства виджетов поддерживаемыми. Это все, что нам нужно сделать, чтобы добавить поддержку виджета Экрана Блокировки в наше существующее расширение виджета.

struct ViewSizeWidget: Widget {     var body: some WidgetConfiguration {         StaticConfiguration(             kind: "com.SwiftSenpaiDemo.ViewSizeWidget",             provider: ViewSizeTimelineProvider()         ) { entry in             ViewSizeWidgetView(entry: entry)         }         .configurationDisplayName("View Size Widget")         .description("This is a demo widget.")         .supportedFamilies([             .systemSmall,             .systemMedium,             .systemLarge,             // Add Support to Lock Screen widgets             .accessoryCircular,             .accessoryRectangular,             .accessoryInline,         ])     } }

Однако, если вы попытаетесь показать виджет на экране блокировки, вы заметите, что UI нашего существующего виджета выглядит не очень в этих новых форм-факторах. Более того, для семейства accessorInline невозможно получить даже размер вью.

View Size Widget в новых форм-фактора

Чтобы со всем этим разобраться, нам нужно будет создать 3 отдельных вью SwiftUI для каждого из этих семейств виджетов.

Реализация UI Виджетов Экрана Блокировки

Предположим, что желаемые UI виджетов Экрана Блокировки следующие:

Виджет экрана блокировки размера просмотра

Мы можем реализовать каждый из них так:

/// Widget view for `accessoryInline ` struct InlineWidgetView: View {          var body: some View {         Text("🤷🏻‍♂️ View size not available 🤷🏻‍♀️")     } } /// Widget view for `accessoryRectangular` struct RectangularWidgetView: View {          var body: some View {         GeometryReader { geometry in             ZStack {                 AccessoryWidgetBackground()                     .cornerRadius(8)                 GeometryReader { geometry in                     Text("\(Int(geometry.size.width)) x \(Int(geometry.size.height))")                         .font(.headline)                         .frame(maxWidth: .infinity, maxHeight: .infinity)                 }             }         }     } } /// Widget view for `accessoryCircular` struct CircularWidgetView: View {          var body: some View {         GeometryReader { geometry in             ZStack {                 AccessoryWidgetBackground()                 VStack {                     Text("W: \(Int(geometry.size.width))")                         .font(.headline)                     Text("H: \(Int(geometry.size.height))")                         .font(.headline)                 }             }         }     } }

Обратите внимание, что я использую AccessoryWidgetBackground() в качестве фонового вью для RectangularWidgetView и CircularWidgetView. Это SwiftUI-вью со стандартным внешним видом. Мы можем поместить его в ZStack за контентом виджета, чтобы создать виджеты экрана блокировки с непрозрачным фоном.

Примечание: Если вы создаете отдельный файл SwiftUI для каждого вью виджета, обязательно назначьте им таргет расширения виджета. Кроме того, обязательно импортируйте модуль WidgetKit, если вы используете AccessoryWidgetBackground() в качестве фона.

Со всеми этими SwiftUI-вью мы можем вернуться к реализации ViewSizeWidget и соответствующим образом обновить его вью:

struct ViewSizeWidgetView: View {     let entry: ViewSizeEntry     // Obtain the widget family value     @Environment(\.widgetFamily)     var family     var body: some View {         switch family {         case .accessoryRectangular:             RectangularWidgetView()         case .accessoryCircular:             CircularWidgetView()         case .accessoryInline:             InlineWidgetView()         default:             // UI for Home Screen widget             HomeScreenWidgetView(entry: entry)         }     } }

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

Обработка Отсечения Контента

На этом этапе все выглядит нормально при работе на больших устройствах, таких как iPhone 14 Pro Max. Однако, если мы переключимся на устройства с меньшим размером экрана, такие как iPhone 14, вы заметите, что содержимое встроенного виджета обрезается.

Виджет блокировки экрана с усеченным содержимым

Чтобы сделать это, мы можем использовать ViewThatFits для предоставления другого, меньшего вью, когда большее - обрезается. Вот как:

struct InlineWidgetView: View { var body: some View { ViewThatFits { // Provide 2 subviews for `ViewThatFits` evaluation // Prioritizing from top to bottom Text("🤷🏻‍♂️ View size not available 🤷🏻‍♀️") Text("🤷🏻‍♂️ Nope! 🤷🏻‍♀️") } } }

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

Параллельное сравнение iPhone 14 и iPhone 14 Pro Max.

Исправление усеченного контента в виджете Экрана Блокировки

Таким образом, мы успешно обновили код наших существующих виджетов Главного Экрана для поддержки виджетов Экрана Блокировки. Браво!

Полный образец кода здесь. Пожалуйста!

Вот и все! Создать виджет Экрана Блокировки на самом деле довольно просто.

Была ли эта статья полезной? Если да, не стесняйтесь ознакомиться с другими моими статьями, связанными с разработкой iOS, здесь. Пожалуйста, следите за мной в Twitter и подписывайтесь на мою рассылку, чтобы не пропустить ни одной из моих будущих статей.

Спасибо за прочтение. 👨🏻‍💻

Подписывайся на наши соцсети: Telegram / VKontakte

Вступай в открытый чат для iOS-разработчиков: t.me/swiftbook_chat

0
1 комментарий
Николай Виноградов

Никогда такого не было и вот опять...

Ответить
Развернуть ветку
-2 комментариев
Раскрывать всегда