Section 7/141 menit
7. Widget Families dan Adaptive Layout
7. Widget Families dan Adaptive Layout
Semua Ukuran yang Tersedia
swift
struct AdaptiveWidgetView: View {
@Environment(\.widgetFamily) var family // Deteksi ukuran aktif
let entry: HabitEntry
var body: some View {
switch family {
case .systemSmall:
SmallHabitView(entry: entry)
case .systemMedium:
MediumHabitView(entry: entry)
case .systemLarge:
LargeHabitView(entry: entry)
case .accessoryCircular:
// Lock Screen / Apple Watch circular
CircularHabitView(entry: entry)
case .accessoryRectangular:
// Lock Screen rectangular
RectangularHabitView(entry: entry)
case .accessoryInline:
// Lock Screen inline (satu baris teks)
Text("\(entry.habitName): \(entry.isCompleted ? "✓" : "○")")
@unknown default:
SmallHabitView(entry: entry)
}
}
}
Lock Screen Widgets (iOS 16+)
swift
// Lock Screen widget harus menggunakan .containerBackground yang tepat
struct CircularHabitView: View {
let entry: HabitEntry
var body: some View {
ZStack {
AccessoryWidgetBackground()
VStack(spacing: 0) {
Image(systemName: entry.isCompleted ? "checkmark" : "circle")
.font(.title3)
Text("\(entry.streak)")
.font(.caption2.bold())
}
}
.containerBackground(.clear, for: .widget)
}
}
// Daftarkan ke supported families
struct HabitWidget: Widget {
var body: some WidgetConfiguration {
StaticConfiguration(kind: "HabitWidget", provider: HabitProvider()) { entry in
AdaptiveWidgetView(entry: entry)
}
.supportedFamilies([
.systemSmall,
.systemMedium,
.accessoryCircular, // Lock Screen
.accessoryRectangular, // Lock Screen
.accessoryInline // Lock Screen (teks saja)
])
}
}
Interactive Widgets (iOS 17+)
Widget kini bisa memiliki tombol dan toggle yang langsung menjalankan App Intent:
swift
import AppIntents
// Definisikan intent yang akan dijalankan saat tombol ditekan
struct CompleteHabitIntent: AppIntent {
static var title: LocalizedStringResource = "Tandai Habit Selesai"
static var description = IntentDescription("Menandai habit hari ini sebagai selesai")
@Parameter(title: "Habit ID")
var habitID: String
func perform() async throws -> some IntentResult {
// Kode ini berjalan di proses app extension
let sharedDefaults = UserDefaults(suiteName: "group.com.perusahaan.namaapp")
sharedDefaults?.set(true, forKey: "habitCompleted_\(habitID)")
// Trigger reload widget
return .result()
}
}
// Widget view dengan Button interaktif
struct InteractiveHabitView: View {
let entry: HabitEntry
var body: some View {
VStack(spacing: 12) {
Text(entry.habitName)
.font(.headline)
// Button ini bekerja langsung di widget — tidak buka app
Button(intent: CompleteHabitIntent(habitID: entry.habitID)) {
Label(
entry.isCompleted ? "Selesai ✓" : "Tandai Selesai",
systemImage: entry.isCompleted ? "checkmark.circle.fill" : "circle"
)
}
.buttonStyle(.borderedProminent)
.disabled(entry.isCompleted)
}
.containerBackground(.fill.tertiary, for: .widget)
}
}