Bu API’leri kendi uygulamalarımda 4 yıl boyunca geliştirdikten sonra, HandySwiftUI’ın ilk etiketli sürümünü paylaşmaktan mutluluk duyuyorum. Bu paket, yalnızca geçen yıl 10 uygulama yayınlamamda büyük rol oynayan çeşitli yardımcı araçlar ve kolaylık API’leri içeriyor. HandySwift paketimin Foundation için sunduğu kolaylıklara benzer şekilde, SwiftUI geliştirme için pratik çözümler sunuyor.
Bu yazıda, TranslateKit, FreemiumKit ve CrossCraft gibi uygulamalarımda günlük geliştirme sürecinde en değerli bulduğum view modifier’ların bir seçkisini paylaşacağım. HandySwiftUI çok daha fazla yardımcı araç içerse de, bu modifier’lar gerçek dünya uygulamalarında defalarca kendini kanıtladı ve senin SwiftUI projelerinde de işine yarayabilir.
Akıllı Renk Kontrastı
foregroundStyle(_:minContrast:) modifier’ı, renk kontrastını otomatik olarak ayarlayarak metnin her zaman okunabilir kalmasını sağlar. Dinamik renkler veya belirli renk şemalarında zayıf kontrasta sahip olabilen .yellow gibi sistem renkleri için kullanışlıdır:
struct AdaptiveText: View {
@State private var dynamicColor: Color = .yellow
var body: some View {
HStack {
// Kontrast ayarı olmadan
Text("Maybe hard to read")
.foregroundStyle(dynamicColor)
// Otomatik kontrast ayarıyla
Text("Always readable")
.foregroundStyle(dynamicColor, minContrast: 0.5)
}
}
}
TranslateKit’teki bu uyarı göstergesi
.yellowkullanıyor ama okunabilir olması için açık modda bile iyi bir kontrast sağlıyor. Karanlık modda sarı kalıyor.
minContrast parametresi (0 ile 1 arasında), parlaklık değerini (algılanan parlaklık) kullanarak beyaza (açık modda) veya siyaha (karanlık modda) karşı minimum kontrast oranını belirler. Bu sayede metin, mevcut renk şemasından bağımsız olarak her zaman okunabilir kalır.
Hata Yönetimli Task’lar
throwingTask modifier’ı, SwiftUI view’larında asenkron hata yönetimini kolaylaştırır. SwiftUI’ın manuel do-catch blokları gerektiren yerleşik .task modifier’ının aksine, throwingTask ayrı bir hata işleme closure’u sunar:
struct DataView: View {
@State private var error: Error?
var body: some View {
ContentView()
.throwingTask {
try await loadData()
} catchError: { error in
self.error = error
}
}
}Task, .task ile benzer şekilde davranır – view göründüğünde başlar ve kaybolduğunda iptal edilir. catchError closure’u isteğe bağlıdır, yani hataları ele almana gerek yoksa atlayabilirsin. Bu, task modifier’ının bıraktığı boşluğu dolduruyor.
Platforma Özel Stilleme
Platform modifier’larının tam seti, çoklu platform UI’ı üzerinde hassas kontrol sağlar:
struct AdaptiveInterface: View {
var body: some View {
ContentView()
// Sadece macOS'te padding ekle
.macOSOnlyPadding(.all, 20)
// Platforma özel stiller
.macOSOnly { $0.frame(minWidth: 800) }
.iOSOnly { $0.navigationViewStyle(.stack) }
}
}Örnek, platforma özel stilleme için modifier’ları sergiliyor:
.macOSOnlyPadding,Formgibi konteynerların varsayılan padding’e sahip olmadığı macOS’te padding ekler.macOSOnlyFrame, macOS’te gereken minimum pencere boyutlarını ayarlarPlatform modifier’ları (
.iOSOnly,.macOSOnly,.iOSExcluded, vb.) iOS, macOS, tvOS, visionOS ve watchOS için mevcut olup belirli platformlarda seçici view değişiklikleri uygulamana olanak tanır
Bu modifier’lar, kodu temiz ve bakımı kolay tutarken platforma uygun arayüzler oluşturmana yardımcı olur.
Köşe Yuvarlatmalı Kenarlık
SwiftUI, köşe yuvarlatması olan bir view’a kenarlık eklemenin doğrudan bir yolunu sunmuyor. Standart yaklaşım, hatırlanması zor olan ayrıntılı overlay kodu gerektirir:
Text("Without HandySwiftUI")
.padding()
.overlay(
RoundedRectangle(cornerRadius: 12)
.strokeBorder(.blue, lineWidth: 2)
)HandySwiftUI bunu kullanışlı bir kenarlık modifier’ı ile basitleştirir:
Text("With HandySwiftUI")
.padding()
.roundedRectangleBorder(.blue, cornerRadius: 12, lineWidth: 2)
Örneğin TranslateKit’teki rozetler yuvarlatılmış kenarlıklar için bunu kullanıyor.
Koşullu Modifier’lar
Koşullu view değişikliklerini temiz bir şekilde ele almak için bir modifier seti:
struct DynamicContent: View {
@State private var isEditMode = false
@State private var accentColor: Color?
var body: some View {
ContentView()
// Koşula göre farklı modifier'lar uygula
.applyIf(isEditMode) {
$0.overlay(EditingTools())
} else: {
$0.overlay(ViewingTools())
}
// Optional varsa modifier uygula
.ifLet(accentColor) { view, color in
view.tint(color)
}
}
}Örnek, bir boolean koşuluna göre farklı view değişiklikleri uygulayan .applyIf‘i ve Swift’in if let ifadesi gibi çalışan – closure’u içinde optional değerlere non-optional erişim sağlayan .ifLet‘i gösteriyor. Her iki modifier da SwiftUI view’larındaki kalıp kodları azaltmaya yardımcı olur.
Uygulama Yaşam Döngüsü Yönetimi
Uygulama durum değişikliklerine zarif bir şekilde yanıt ver:
struct MediaPlayerView: View {
@StateObject private var player = VideoPlayer()
var body: some View {
PlayerContent(player: player)
.onAppResignActive {
// Uygulama arka plana geçtiğinde oynatmayı duraklat
player.pause()
}
.onAppBecomeActive {
// Uygulama aktif olduğunda durumu kontrol et
player.checkPlaybackState()
}
}
}Bu modifier’lar birlikte çalışarak daha akıcı ve bakımı kolay bir SwiftUI geliştirme deneyimi oluşturur, kalıp kodları azaltırken kullanıcı arayüzünün kalitesini ve tutarlılığını artırır.
Silme Onay Diyalogları
SwiftUI’ın onay diyalogları, silme işlemleri için tekrarlayan kalıp kodları gerektirir, özellikle bir listeden öğe silerken:
struct TodoView: View {
@State private var showDeleteConfirmation = false
@State private var todos = ["Buy milk", "Walk dog"]
@State private var todoToDelete: String?
var body: some View {
List {
ForEach(todos, id: \.self) { todo in
Text(todo)
.swipeActions {
Button("Delete", role: .destructive) {
todoToDelete = todo
showDeleteConfirmation = true
}
}
}
}
.confirmationDialog("Are you sure?", isPresented: $showDeleteConfirmation) {
Button("Delete", role: .destructive) {
if let todo = todoToDelete {
todos.removeAll { $0 == todo }
todoToDelete = nil
}
}
Button("Cancel", role: .cancel) {
todoToDelete = nil
}
} message: {
Text("This delete action cannot be undone. Continue?")
}
}
}HandySwiftUI bunu özel bir modifier ile basitleştirir:
struct TodoView: View {
@State private var todoToDelete: String?
@State private var todos = ["Buy milk", "Walk dog"]
var body: some View {
List {
ForEach(todos, id: \.self) { todo in
Text(todo)
.swipeActions {
Button("Delete", role: .destructive) {
todoToDelete = todo
}
}
}
}
.confirmDeleteDialog(item: $todoToDelete) { item in
todos.removeAll { $0 == item }
}
}
}
CrossCraft’ta kazara silmeleri önlemek için onay diyaloğuyla bulmaca silme.
Örnek, .confirmDeleteDialog’un tüm silme akışını – onaydan uygulamaya kadar – tek bir modifier ile nasıl ele aldığını gösteriyor. Diyalog yaklaşık 40 dilde otomatik olarak yerelleştirilmiş ve platform tasarım kurallarına uyar. Farklı bir mesaj sağlaman gerekiyorsa isteğe bağlı bir message parametresi verebilirsin. Ayrıca liste içermeyen durumlar için boolean alan bir overload da mevcut.
Hemen Başla
Bu modifier’ları projelerinde benim kadar faydalı bulacağını umuyorum. İyileştirme fikirlerin veya SwiftUI topluluğuna katkı sağlayabilecek ek modifier’lar için GitHub’da katkıda bulunmaktan çekinme:
github.comFlineDev / HandySwiftUIHandy SwiftUI features that didn’t make it into SwiftUI (yet)
Bu yazı, HandySwiftUI’ın özelliklerini keşfeden dört makaleden oluşan serinin ikincisi. Henüz okumadıysan Yeni Tipler hakkındaki önceki makaleye göz at ve Extension’lar ile Stiller hakkındaki yaklaşan yazıları takipte kal!

