SwiftUI: Widget

Orkun Akkaya
ÇSTech
Published in
4 min readDec 29, 2022

Apple, iOS 14 ile birlikte SwiftUI ile dizayn edilen ana ekran widgetlarını tanıttı. Bu widgetlar sayesinde kullanıcılar uygulamaya girmeden onlara minimal bilgiler sunabiliriz.

developer.apple.com/widgets/

Bu yazımızda temel olarak widget oluşturmayı inceleyeceğiz.

Gereklilikler:

1.MacOS 10.15.5 ve üzeri.

2.Xcode 12 ve üzeri.

Kurulum:

Öncelikle uygulamamıza widget extension ekleyelim:

Xcode -> File -> New -> Target

Widgetı eklemeden önce dikkat etmemiz gereken bir nokta ise Include Configuration Intent seçeneğidir. Bu seçenek kullanıcı tarafından yapılandırılabilen bir özelliği olan veya olmayan widgetlar için belirleyici olacaktır. Şimdilik işaretlemeden devam edeceğiz fakat kullanıcı tarafından yapılandırılabilen özellikli widget oluşturmaya da göz atacağız.

Eklerken widgetımızı activate olarak seçelim.

Eklenen widget target’ı içerisindeki swift dosyasında sistem bize hazır bir taslak vermekte. Bunun içerisindeki yapıları incelemekte fayda var.

struct WidgetExampleEntryView : View {
var entry: Provider.Entry

var body: some View {
Text(entry.date, style: .time)
}
}

View içerisinde widget için SwiftUI tasarım öğelerini kullanarak ihtiyaçlarımıza göre tasarımımızı oluşturabiliriz. supportedFamilies fonksiyonunu kullanarak büyük, orta ve küçük ölçekte widgetlar oluşturulabilir.

var body: some WidgetConfiguration {
// Configuration Intent'i dahil etmediğimiz için StaticConfiguration
StaticConfiguration(kind: kind, provider: Provider()) { entry in
WidgetExampleEntryView(entry: entry)
}
.configurationDisplayName("My Widget")
.description("This is an example widget.")
.supportedFamilies([.systemSmall,.systemMedium, .systemLarge])
}
struct Provider: TimelineProvider {
func placeholder(in context: Context) -> SimpleEntry {
SimpleEntry(date: Date())
}

func getSnapshot(in context: Context, completion: @escaping (SimpleEntry) -> ()) {
let entry = SimpleEntry(date: Date())
completion(entry)
}

func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
var entries: [SimpleEntry] = []

// Generate a timeline consisting of five entries an hour apart, starting from the current date.
let currentDate = Date()
for hourOffset in 0 ..< 5 {
let entryDate = Calendar.current.date(byAdding: .hour, value: hourOffset, to: currentDate)!
let entry = SimpleEntry(date: entryDate)
entries.append(entry)
}

let timeline = Timeline(entries: entries, policy: .atEnd)
completion(timeline)
}
}

Provider, TimelineProvider protokolünü uygulayan ve widget’in ne zaman işleneceğini WidgetKit’e bildiren bir zaman çizelgesi oluşturur.

Placeholder fonksiyonu widget’ın genel görünümünün temsilini oluşturmak içindir.

getSnapshot ise geçişlerde kullanılır, kullanıcı widget galerisinde widget’ı görüntülediğinde veya ana ekrana eklediğinde bir geçiş oluşur bu sırada ekranda widget datalarının gözükmesini sağlar. Bu nedenle olabildiğince hızlı şekilde verileri aktaracak şekilde ayarlanmalıdır. Servis çağrısı gibi işlemler bu durumu yavaşlatacağı için snapshot bu kullanıma uygun değildir.

getTimeline fonksiyonu için widget’ın zaman yönetimini kontrol altına alır diyebiliriz. Widget’a ne zaman kendisini güncellemesi gerektiği bilgisini bu fonksiyon üzerinden yönetebiliriz, bu sayede widget zamanın farklı noktalarında neyi göstermesi gerektiğini yapılandırır. Copmletion çağrıldığında verileri gösterecek şekilde asenkron çağrıları da burada yapılandırabiliriz.

Kullanıcı tarafından yapılandırılabilen widgetlar:

Başlangıçta widget’ı projeye eklerken Include Configuration Intent seçeneğini işaretlememiştik, şimdi o seçeneği etkin hale getirip kullanıcının widget’a veri girebildiği dinamik bir widget oluşturacağız. Bu seçenekte oluşturulan swift dosyasına dikkat ederseniz diğerinden birkaç farklı noktası olacaktır. Dosya Intents kütüphanesini dahil eder ve SimpleEntry modeli içerisinde artık ConfigurationIntent tipinde bir değerimiz var. Kullanıcının widget’a yollayacağı verilere bu parametre üzerinden erişebileceğiz.

import Intents

struct SimpleEntry: TimelineEntry {
let date: Date
let configuration: ConfigurationIntent
}

struct ExampleWidgetEntryView : View {
var entry: Provider.Entry

var body: some View {
VStack {
Text("String: \(entry.configuration.StringParameter ?? "")")
Text("Int: \(entry.configuration.IntParameter?.description ?? "")")
Text("Bool: \(entry.configuration.BoolParameter?.boolValue.description ?? "")")
}
}
}

Ayrıca klasörler altına bir de intent definition dosyası eklendi, yapılandırmaları bu dosya üzerinden yapabileceğiz.

Burada örnek olarak Int, String ve Bool olarak örnek üç parametre ekledim. Burada siz kendi özel tiplerinizi de oluşturup dahil edebilirsiniz. Ayrıca oluşturduğunuz parametrelerle ilgili yapılandırmalar yapabilirsiniz. Örneğin Int tipinde parametre için type olarak Stepper seçerseniz kullanıcıya sadece artırıp eksiltmesini sağlayacak şekilde işlem yaptırılacaktır. Şimdi bu parametrelere kullanıcının nasıl veri gireceğine bakalım. Bunun için widget’ı uygulamaya ekledikten sonra Araç Takımını Düzenle seçeneğini kullanacağız veya widget ekleme ekranında ana ekrana widget’ı ekledikten sonra düzenlemeyi sonlandırmadan widget üzerine dokunursanız da düzenleme ekranı gelecektir.

Düzenleme ekranında kullanıcı görseldeki gibi veri girişi yapar. Bu şekilde kullanıcının veri girdiği widgetlar oluşturabilir, widget’ı dinamik hale getirebilirsiniz. Mesela hangi şehirde olduğunu seçen bir kullanıcının şehre özel hava durumunu gördüğü widgetlar bu kullanıma bir örnek olabilir.

Kaynaklar:

--

--