WWDC21: SwiftUI’a Gelen Yenilikler
Gelen yeniliklerle birlikte daha özelleştirilebilir olan SwiftUI’yı birlikte inceleyelim.
Giriş
SwiftUI, WWDC19’da ilk ortaya çıkışından beri bütün geliştiricilerin ilgisini çekmişti. WWDC20’de bu ilgi WidgetKit gibi bazı “framework”ler ile sadece SwiftUI kullanılabileceği söylendiğinde daha da arttı. WWDC21’de tanıtılan yenilikler ile birlikte SwiftUI gerçekten farklı bir boyuta geldiğini söylemem gerekiyor.
Makalenin başında söylemem gerekiyor ki burada WWDC21’de SwiftUI hakkında sadece tanıtılan temel yenilikleri bulabilirsiniz. İlerleyen zamanlarda yayınlayacağımız makalelerde bu yeniliklere değiniyor olacağız.
List
List
objesi bir SwiftUI uygulamasındaki en yaygın obje olabilir. Bu sene List
objesine gelen yenilikler ile birlikte neredeyse UITableView
objesini aratmayacak gibi. Tabii ki hâlâ UITableView
objesinin bütün özelliklerini barındırmıyor.
Genel olarak bu sene gelen yeniliklerle birlikte List
objesinin içerisindeki hücrelerin stillerini ve renklerini özelleştirebiliyoruz. Bunun için gelen yeni “modifier”ları aşağıdan görebilirsiniz:
struct ContentView: View {
// MARK: - Properties
@State private var names = ["Sergen", "Ege", "Can", "Berkin", "Emirhan"]
// MARK: - View
var body: some View {
NavigationView {
List {
ForEach(names, id: \.self) { name in
Text(name)
}
}
.listRowSeparator(.visible, edges: .all)
.listSectionSeparator(.visible, edges: .all)
.listSectionSeparatorTint(Color.purple)
.listRowSeparatorTint(Color.red)
.navigationTitle("Names")
}
}
}
Ayrıca, artık List
objesinin içerisindeki hücreleri kaydırırken ortaya çıkan butonların görünümlerini ve konumlarını değiştirebiliyoruz.
struct ContentView: View {
// MARK: - Properties
@State private var names = ["Sergen", "Ege", "Can", "Berkin", "Emirhan"]
// MARK: - View
var body: some View {
NavigationView {
List {
ForEach(names, id: \.self) { name in
Text(name)
.swipeActions(edge: .trailing, allowsFullSwipe: false) {
Button("Remove", role: .destructive) {
names.removeAll { $0 == name }
}
}
.swipeActions(edge: .leading, allowsFullSwipe: true) {
Button("Copy") {
names.append(name)
}
}
}
}.navigationTitle("Names")
}
}
}
SF Symbols & TabView
Apple’ın Human Interface Guidelines’ında yazdığı gibi bir Tab Bar objesinin bir elemanı seçildiği zaman o eleman diğerlerine göre daha dolu ve canlı gözükmelidir. Eskiden SwiftUI’ın TabView
objesinin elemanlarının Human Interface Guidelines’a uygun olması için elemanların görsellerinde kullanılan SF Symbols elemanlarını “fill” olarak seçerdik. Bu yıl ile birlikte artık SF Symbols’ın “fill” hallerini kullanmak zorunda değiliz çünkü SwiftUI o anki duruma göre sembolleri yönetebiliyor.
Bu şekilde Tab Bar ikonlarınızın çok yönlü olmasını sağlayabilirsiniz. Örneğin üstteki TabView
objesindeki elemanların görsellerini yukarıda bahsettiğimiz şekilde bir Mac uygulaması olarak çalıştırırsak ikonların Mac platformuna özel şekilde değiştiğini görürsünüz.
Refreshable
Bu sene ile birlikte güncellenebilen veya yeniden getirilebilen ekranların içerisindeki elemanları yenilemek için artık refreshable
“modifier”ını kullanabiliriz. Bu sayede yukarında aşağıya doğru kaydırma hareketi ile bir liste içerisindeki elemanları yineleyebiliriz:
struct ContentView: View {
// MARK: - Properties
@State private var names = ["Sergen", "Ege", "Can", "Berkin", "Emirhan"]
// MARK: - View
var body: some View {
NavigationView {
List {
ForEach(names, id: \.self) { name in
Text(name)
}
}
.refreshable {
names.append("❤️ TurkishKit")
}
.navigationTitle("Names")
}
}
}
Refreshable ile alakalı yazdığımız yazıya aşağıdan ulaşabilirsiniz:
TimelineView
TimelineView
bu yıl hayatımıza giren bir başka SwiftUI objesidir. Eskiden SwiftUI ile oluşturulmuş bir ekranın içerisindeki bir değerin değiştirilmesi için @State
veya @Environment
“property wrapper”larını kullanmak gerekiyordu. Artık TimelineView
ile tanımladığınız belli saatler arasında UI elemanlarınızı güncelleyebiliyoruz.
Bir saat uygulamasında kullanabilecek bir örnek verelim:
TimelineView(PeriodicTimelineSchedule(from: startDate, by: 1)) { context in
AnalogTimerView(date: context.date)
}
Text
Bunu sene tanıtılan AttributedString
değer türü ile birlikte artık Text
içerisinde kullandığımız String
elemanlarını renklerini ve stillerini özelleştirebiliyoruz ve hatta ekstra işlevler tanımlayabiliyoruz.
Aşağıdaki örnekte AttributedString
ile bir String
değerini markdown haline getiriyoruz:
struct ArticleView: View {
// MARK: - Properties
let post: Article
var markdown: AttributedString {
try! AttributedString(markdown: article.content)
}
// MARK: - View
var body: some View {
HStack {
AsyncImage(url: article.image)
VStack {
Text(article.title)
Text(markdown)
}
}
}
}
Ayrıca Text
objesi içerisinde yazdığımız String
elemanlarında markdown operatörleri kullanabiliyoruz.
struct ContentView: View {
// MARK: - View
var body: some View {
Text("**TurkishKit ❤️**")
}
}
AttributedString
ile alakalı yazdığımız makaleye buradan ulaşabilirsiniz:
Search
Ben de geçen seneden beri SwiftUI’a UIKit’in UISearchBar
objesinin bir benzerinin gelmesini bekliyordum. Bu sene sonunda searchable
“modifier”ı ile çok kolay bir şekilde arama çubuğu ve arama özelliklerini kullanabiliyoruz.
struct ContentView: View {
// MARK: - Properties
@State private var query: String = ""
@State private var names = ["Sergen", "Ege", "Can", "Berkin", "Emirhan"]
// MARK: - View
var body: some View {
NavigationView {
List {
ForEach(names, id: \.self) { name in
Text(name)
}
}
.searchable("Search term", text: $query, placement: .automatic)
.onChange(of: query) { print($0) }
.navigationTitle("Messages")
}
}
}
AsyncImage
AsyncImage
objesi URLSession
kullanarak internetten görsel indirmenize ve sunmanıza olanak tanır. Kullanımı oldukça kolay olduğunu söyleyebilirim.
struct Article: Hashable {
// MARK: - Properties
let image: URL
let title: String
let content: String
}
struct ArticleView: View {
// MARK: - Properties
let article: Article
// MARK: - View
var body: some View {
HStack {
AsyncImage(url: article.image)
VStack {
Text(article.title)
Text(article.content)
.foregroundColor(.secondary)
}
}
}
}
AsyncImage
ile alakalı yazdığımız yazıya aşağıdan ulaşabilirsiniz:
@FocusState
WWDC21’de tanıtılan “Odak” modunu SwiftUI uygulamarımıza da entegre etmek için Apple bize @FocusState
adında bir “property wrapper” sunuyor. Bu sayede @FocusState
ile tanımlanan değere göre uygulamanızın özelliklerini değiştirebilirsiniz.
struct LoginForm: View {
// MARK: - Enumerations
enum Field: Hashable {
case usernameField
case passwordField
}
// MARK: - Properties
@State private var username = ""
@State private var password = ""
@FocusState private var focusedField: Field?
// MARK: - View
var body: some View {
VStack {
TextField("Username", text: $username)
.focused($focusedField, equals: .usernameField)
SecureField("Password", text: $password)
.focused($focusedField, equals: .passwordField)
Button("Sign In") {
if username.isEmpty {
focusedField = .usernameField
} else if password.isEmpty {
focusedField = .passwordField
} else {
handleLogin(username, password)
}
}
}
}
}
Material
Bu sene iOS’te derinlik hissi uyandırmak için kullanabileceğiniz yarı saydam bir efekt oluşturan Material
objelerini kullanabiliriz. SwiftUI’ın önceki sürümlerinde Material
objelerine erişimimiz yoktu. Neyse ki SwiftUI’ın yeni sürümü bize ultra inceden ultra kalına kadar çok çeşitli objeler sağlıyor.
Örnek olarak Label
objesinin verilebilir:
ZStack {
Color.teal
Label("Flag", systemImage: "flag.fill")
.padding()
.background(.regularMaterial)
}
Sonuç
Makalenin başında da söyleceğimiz. Kişisel olarak SwiftUI şu anki haliyle yeni bir uygulama geliştirmek veya önceden geliştirilmiş bir uygulamanın yeni bölümlerini geliştirmek için oldukça elverişli.
iOS 15’in -Türkiye dahil 😅- dünya genelinde iOS ekosistemini domine etmeye başladığı zaman “production-ready” projelerimde SwiftUI’ı sonuna kadar kullanmak için sabırsızlanıyorum!