Widget — компактен елемент на потребителския интерфейс на началния екран на iOS или Android, показващ актуална информация без отваряне на приложението. Уиджетите се появиха в iOS 14 (2020) с рамката WidgetKit на SwiftUI и съществуват в Android от версия 1.5 чрез AppWidgetProvider на Kotlin или Java. Те позволяват на потребителя да вижда времето, валутния курс, календара, бележките или статуса на задачите директно на home screen, спестявайки време и опростявайки взаимодействието. Според данни на Apple потребителите взаимодействат с уиджети средно 4–6 пъти на ден, а инсталирането на уиджет на home screen увеличава ангажираността с приложението с 20–30%. За Android тези показатели са сравними. Нека разгледаме как работят уиджетите на двете платформи, с примери за код.
Основни точки
Widget — компактен елемент на потребителския интерфейс, поставен на началния екран на мобилно устройство за показване на актуална информация от приложението. За разлика от пълноценно приложение, уиджетът не изисква стартиране — потребителят вижда данните веднага след отключване на екрана. Концепцията идва от десктоп ОС (macOS Dashboard, Windows Sidebar), а в мобилната среда Android въведе поддръжка за уиджети от първото си издание (Android 1.5 Cupcake, 2009), докато Apple ги добави едва в iOS 14.
Техническа реализация се различава на платформите. iOS уиджет — отделен target в Xcode, компилиран като Widget Extension, работещ в собствен процес. Android уиджет — BroadcastReceiver, разширяващ AppWidgetProvider, рендериращ оформлението чрез RemoteViews. И двете платформи налагат строги ограничения върху производителността и паметта: системата може да премахне уиджета при превишаване на лимитите. iOS ограничава уиджета до 30–50 MB и време за формиране на timeline до 10 секунди. Android — интервал на обновяване не по-често от 30 минути чрез updatePeriodMillis и принудително прекратяване при замръзване на onUpdate.
Smart Stack на iOS и стекове на Android позволяват на потребителя да групира няколко уиджета в един слот на home screen. Устройството автоматично превключва показания уиджет в зависимост от времето на деня, местоположението или активността на потребителя. Например сутрин се показва календар, през деня — време, вечер — бележки. За разработчика това означава, че уиджетът трябва да работи правилно в рамките на стек и да не разчита на постоянно показване.
WidgetKit — рамката на Apple за създаване на уиджети на iOS 14+, iPadOS 14+ и macOS 11+. Уиджетите се пишат на Swift, използвайки SwiftUI и механизма TimelineProvider за обновяване на съдържанието. Архитектурата на WidgetKit включва три компонента: TimelineProvider — източник на данни, формиращ времева линия със записи; Widget Entry View — SwiftUI View, показваща съдържание въз основа на запис; Widget Configuration — описание на типа, размерите и семействата на уиджета.
WidgetKit сам управлява обновяването — разработчикът не може принудително да обновява уиджета по-често отколкото системата позволява. Apple използва бюджет за обновяване, разпределян между всички уиджети на устройството. Минимален интервал — 15–30 минути. За критични данни (например статус на доставка) се използва push-to-refresh — изпращане на push известие с подходяща дата в бъдещето, след което WidgetKit изисква нова времева линия. От iOS 17 уиджетите поддържат интерактивност чрез App Intents — потребителят може да натиска бутони вътре в уиджета без да отваря приложението.
| Размер | iOS (pt) | Съдържание |
|---|---|---|
| Small | 170 × 170 | 1–2 показателя: температура, валутен курс |
| Medium | 360 × 170 | 2–4 показателя: прогноза за 5 дни, списък |
| Large | 360 × 380 | 5–10 показателя: календар, новинарска емисия |
AppWidgetProvider — базов клас за създаване на уиджети в Android, част от Android SDK от API level 3. Android уиджет — BroadcastReceiver, получаващ събития за обновяване, активиране и деактивиране от системата. За разлика от iOS, където уиджетът се рисува от SwiftUI на собствен двигател, Android използва RemoteViews — ограничен набор от View компоненти, рендерирани в системния процес (launcher). Не могат да се използват персонализирани View, сложни анимации или UI библиотеки.
Схема на работа на Android уиджет включва четири етапа. Конфигурация — при добавяне на уиджета на екрана системата може да стартира Activity за конфигурация, където потребителят избира настройки. Обновяване — системата извиква onUpdate() на зададен интервал (минимум 30 минути) или по сигнал от приложението чрез WorkManager. Рендериране — доставчикът създава RemoteViews с актуални данни и ги предава чрез AppWidgetManager. Обработка на кликвания — уиджетът поддържа PendingIntent за бутони: при кликване се отваря Activity или се изпълнява действие. От Android 12 уиджетите получиха поддръжка за заоблени ъгли, адаптивни цветове Material You и динамична промяна на размера.
Нека създадем уиджет за времето на Swift, използвайки WidgetKit и SwiftUI. Проектът трябва да съдържа Widget Extension target, добавен в Xcode чрез File → New → Target → Widget Extension.
import WidgetKit
import SwiftUI
// 1. Модел на данни за уиджета
struct WeatherEntry: TimelineEntry {
let date: Date
let temperature: Int
let condition: String
}
// 2. TimelineProvider — доставчик на данни
struct Provider: TimelineProvider {
func placeholder(in context: Context) -> WeatherEntry {
WeatherEntry(date: Date(), temperature: 22, condition: "Слънчево")
}
func getSnapshot(in context: Context, completion: @escaping (WeatherEntry) -> Void) {
let entry = WeatherEntry(date: Date(), temperature: 22, condition: "Слънчево")
completion(entry)
}
func getTimeline(in context: Context, completion: @escaping (Timeline<WeatherEntry>) -> Void) {
let now = Date()
let entry = WeatherEntry(date: now, temperature: 20, condition: "Облачно")
let nextUpdate = Calendar.current.date(byAdding: .hour, value: 3, to: now)!
let timeline = Timeline(entries: [entry], policy: .after(nextUpdate))
completion(timeline)
}
}
// 3. SwiftUI View за показване
struct WeatherWidgetEntryView: View {
var entry: WeatherEntry
var body: some View {
VStack(alignment: .leading) {
Text("Време").font(.caption).foregroundColor(.secondary)
HStack {
Text("\(entry.temperature)°").font(.largeTitle).fontWeight(.bold)
Spacer()
Text(entry.condition).font(.body)
}
}.padding()
}
}
// 4. Конфигурация на уиджета
@main
struct WeatherWidget: Widget {
var body: some WidgetConfiguration {
StaticConfiguration(kind: "WeatherWidget", provider: Provider()) { entry in
WeatherWidgetEntryView(entry: entry)
}
.configurationDisplayName("Време")
.description("Показва текущата температура")
.supportedFamilies([.systemSmall, .systemMedium])
}
}
Ключови моменти: TimelineProvider определя кога и какви данни да се показват; getTimeline създава времева линия с политика за обновяване .after(nextUpdate); StaticConfiguration свързва доставчика с View. За динамични данни се използва IntentConfiguration с потребителски параметри.
Имплементираме подобен уиджет за времето на Kotlin с AppWidgetProvider и RemoteViews.
// WeatherWidgetProvider.kt
class WeatherWidgetProvider : AppWidgetProvider() {
override fun onUpdate(
context: Context,
appWidgetManager: AppWidgetManager,
appWidgetIds: IntArray
) {
for (appWidgetId in appWidgetIds) {
updateAppWidget(context, appWidgetManager, appWidgetId)
}
}
override fun onAppWidgetOptionsChanged(
context: Context,
appWidgetManager: AppWidgetManager,
appWidgetId: Int,
newOptions: Bundle
) {
updateAppWidget(context, appWidgetManager, appWidgetId)
}
companion object {
fun updateAppWidget(
context: Context,
appWidgetManager: AppWidgetManager,
appWidgetId: Int
) {
val temperature = 20
val condition = "Облачно"
val views = RemoteViews(
context.packageName, R.layout.widget_weather
).apply {
setTextViewText(R.id.tv_temperature, "$temperature°")
setTextViewText(R.id.tv_condition, condition)
setOnClickPendingIntent(
R.id.widget_root, PendingIntent.getActivity(
context, 0,
Intent(context, MainActivity::class.java),
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
)
)
}
appWidgetManager.updateAppWidget(appWidgetId, views)
}
}
}
// AndroidManifest — регистрация на доставчика
<receiver android:name=".WeatherWidgetProvider" android:exported="true">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/widget_weather_info" />
</receiver>
Файлът widget_weather_info.xml задава минимални размери, интервал и оформление: minWidth 160dp, minHeight 80dp, updatePeriodMillis 3600000 (1 час). RemoteViews използва само поддържани компоненти: LinearLayout, TextView, ImageView. Персонализирани View и ConstraintLayout са достъпни от Android 5.0. GlanceLayout — експериментален Compose подход за RemoteViews — се появи в Android 15.
Често задавани въпроси
iOS WidgetKit предлага три фиксирани размера (small, medium, large) и обновяване чрез TimelineProvider с бюджет на системата. Android AppWidgetProvider използва гъвкави размери в клетки, RemoteViews за рендериране и обновяване чрез updatePeriodMillis или WorkManager.
На iOS обновяването се контролира от системата — минимален интервал 15–30 минути, за критични данни се използва push-to-refresh. На Android интервалът се задава в updatePeriodMillis (минимум 30 минути) или чрез WorkManager за фонови задачи.
iOS поддържа small (170x170pt), medium (360x170pt) и large (360x380pt). Android използва гъвкава мрежа от клетки: от 2x1 до 5x5, размерът зависи от launcher и версията на ОС.
На iOS анимациите вътре в уиджета липсват — това са статични елементи. Android RemoteViews поддържа основни анимации чрез setFloat() и ViewPropertyAnimator, но сложни анимации не са достъпни.
При правилно имплементиране — не. Системата строго ограничава ресурсите на уиджета: на iOS бюджет за обновяване и памет (30–50 MB), на Android — минимален интервал на обновяване. Проблемните уиджети се прекратяват принудително от системата.
Резюме
Ще разработим мобилно приложение под ключ
IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.
Прочетете също