Tüm Yazılar
KategoriSwiftUI
Okuma Süresi
9 dk
Yayın Tarihi
2026-10-15
Kelime Sayısı
1.335kelime

Kahveni hazırla - bu içerikli bir makale!

LiquidGlassKit: OS 26 ve öncesi için tek bir glass modifier'ı

Özet

LiquidGlassKit OS 26'da native Liquid Glass, iOS 17'ye kadar Material yedeği çiziyor. Render testlerinin gerçekten kırılabilir hâle gelişi.

  • SwiftUI'ın Liquid Glass API'si glassEffect yalnız iOS, macOS, tvOS ve watchOS 26'da var.
  • SwiftUI'ın glass API'sini yalnız Surface klasörü çağırıyor: glassSurface(_:in:interactive:) modifier'ı ve native path'te GlassEffectContainer'ı saran GlassGroup.
  • Paketi Xcode'da File › Add Package Dependencies… menüsünden https://github.com/muhittincamdali/LiquidGlassKit.git adresiyle ya da Package.swift üzerinden ekleyin.
  • Güncel sürüm 2.1.0; README'ye göre sonraki bir sürüm henüz planlanmış değil.
LiquidGlassKit: OS 26 ve öncesi için tek bir glass modifier'ı

İçindekiler


Problem

SwiftUI'ın Liquid Glass API'si glassEffect yalnız iOS, macOS, tvOS ve watchOS 26'da var.

Kullanıcılarının bir kısmı hâlâ iOS 17 ya da 18'de olan bir uygulamanın iki seçeneği kalıyor: glass

görünümünü yalnız OS 26 kullanıcılarına göstermek ya da her glass görünümünü

if #available(iOS 26, *) dalına alıp else kısmına elle bir alternatif yazmak. İkinci yol aynı

kararı bütün ekranlara dağıtıyor ve her kopya zamanla farklılaşıyor: bir kart temayı unutuyor,

bir diğeri Reduce Transparency'yi yok sayıyor.

LiquidGlassKit bu kararı tek bir yere topluyor. OS 26'da SwiftUI'ın native glassEffect'iyle

çiziyor; daha eski sürümlerde (iOS 17, macOS 14, tvOS 17 ve watchOS 10'a kadar) ve visionOS'ta

tint, kenar highlight'ı ve gölgeyle bir Material blur çiziyor. Her OS için aynı kodu

yazıyorsunuz. Paketin üçüncü taraf bağımlılığı yok ve yalnız Swift Package Manager ile dağıtılıyor.

Tasarım kararları ve alternatifler

Tek giriş noktası. SwiftUI'ın glass API'sini yalnız Surface klasörü çağırıyor:

glassSurface(_:in:interactive:) modifier'ı ve native path'te GlassEffectContainer'ı saran

GlassGroup. Bütün bileşenler (butonlar, kartlar, metin alanları, arama çubuğu, tab bar,

navigation bar, sheet ve 2.1.0'dan beri switch ile slider) glassSurface üzerinden çiziliyor.

Alternatif, her bileşende ayrı bir #available dalıydı. Şubat 2026 kodunda ortak bir yol yoktu:

her bileşen kendi Material dolgusunu çiziyordu (.fill(material.swiftUIMaterial)) ve 2.0.0

changelog'u sonucunu kaydediyor: temayı okumayan bileşenler. Tek giriş noktasının bedeli de var:

bileşene özel bir görünüm, örneğin butonlar için farklı bir gölge, ortak modifier'a yeni bir

parametre eklemeyi gerektiriyor.

SwiftUI'ın adlarından uzak durmak. Şubat kodu kendi glassEffect(_:) ve

GlassEffectContainer tanımlarını içeriyordu. OS 26 SDK'sında bu adlar SwiftUI'ınkilerle

çakışıyor; iki modülü birlikte import eden dosyada çağrılar belirsizleşiyor. Yeniden kurulan API

glassSurface, GlassGroup ve LiquidGlassButtonStyle adlarını kullanıyor (SwiftUI'da zaten bir

GlassButtonStyle var). Kullanıcı Apple'ın adları yerine paketin adlarını öğreniyor; README ve

DocC kataloğu her birinin hangi SwiftUI API'sini sardığını yazıyor.

Rendering path'i üç katmanda seçmek. #if os(visionOS) native çağrıyı derleme anında

kaldırıyor, çünkü SwiftUI glassEffect'i orada unavailable işaretliyor. Çalışma anındaki

#available(iOS 26, macOS 26, tvOS 26, watchOS 26, *) kontrolü aynı binary'nin hem iOS 17'de hem

iOS 26'da çalışmasını sağlıyor. Bunun üstünde GlassRenderingMode (automatic ya da fallback)

environment'tan geliyor; uygulama tutarlı bir görünüm için yedek path'i zorlayabiliyor. Kararın

kendisi saf bir fonksiyon, resolved(nativeAvailable:), ve dört kombinasyonun hepsi testli. İlk

tasarımda bir .native case'i de vardı; 2.0.0'dan önce kaldırıldı (6293c16), çünkü her durumda

.automatic gibi davranıyordu: native glass olmayan yerde zorlanamaz, olan yerde .automatic

zaten onu kullanır.

Şekiller için enum. GlassShape generic bir Shape değil, bir enum (yuvarlak köşeli

dikdörtgen, capsule, daire, köşe başına yarıçap). Böylece GlassTheme içinde saklanabiliyor,

testlerde Hashable olarak karşılaştırılabiliyor ve Swift 6'da Sendable oluyor. Çizim

AnyShape üzerinden yapılıyor. Ödünleşim gerçek: kendi şeklinizi veremiyorsunuz.

Özel view yerine stil. Butonlar bir ButtonStyle (.buttonStyle(.liquidGlass)), switch bir

ToggleStyle (.toggleStyle(.liquidGlass)). Basılı durum configuration.isPressed'den geliyor;

klavye ve VoiceOver davranışı normal bir SwiftUI Button ya da Toggle ile aynı kalıyor.

GlassButton aynı stili kullanan bir kolaylık view'ı olarak duruyor.

Kopyalayarak değil, yeniden yazarak birleştirmek. 2.1.0 sürümü aynı yazara ait, MIT lisanslı

iki eski repodan, LiquidGlass-Playground ve GlassmorphismUI'dan fikirler alıyor. Bu repoların kodu

kendi Material katmanlarını çiziyor, haptics ve erişilebilirlik kontrolleri için UIKit'e

bağlanıyordu. Dosyaları kopyalamak yerine her özellik (switch, slider, gradient glass,

highlight'lar, hazır temalar, kod dışa aktarıcı, tab badge'leri) glassSurface üstüne yeniden

yazıldı; böylece temayı, rendering mode'u ve Reduce Transparency'yi kendiliğinden alıyor. Bazı

özellikler bilerek dışarıda kaldı: blur radius, refraction ve saturation değerlerinin native glass'ta

karşılığı yok. swift package diagnose-api-breaking-changes v2.0.0 kırıcı değişiklik bildirmiyor,

bu yüzden sürüm minor. İki kaynak repo artık arşivde ve buraya yönlendiriyor.

Ters giden şey: hiçbir şeyi test etmeyen yeşil kontroller

Reponun geçmişi düz bir çizgi değil ve changelog bunu açıkça yazıyor. Şubat 2026'daki v1.0.0

tag'i derlenmeyen ilk commit'i gösteriyor. Haziran 2026'da kaynağın çoğu yer tutucularla

değiştirildi, test target'ı var olmayan tiplere başvuruyordu ve test ile lint adımlarında

continue-on-error olduğu için CI yine yeşil görünüyordu. 2.0.0 paketi yeniden kurdu ve bu

ayarı kaldırdı; bugün repodaki hiçbir workflow bunu kullanmıyor.

Asıl faydalı ders, yeniden kurulumdan sonra, yeni testler zaten yeşilken geldi. Kaynaktan

özellikleri tek tek silmek, suite'in bunu fark etmediğini gösterdi. 36ef829 commit'i durumu

anlatıyor: sheet testi yalnız görüntü genişliğini frame genişliğiyle karşılaştırıyordu, oysa bu

genişliği zaten frame veriyor. GlassCard ya da GlassTabBar'dan glass'ı kaldırmak, kartın

material'ını yok saymak ya da koyu glass butonlardaki etiketleri yeniden siyah çizmek suite'i yeşil

bırakıyordu. Testler piksel sayımına çevrildi. Görünümler beyaz bir canvas üzerinde yeşil temayla

çiziliyor ve her bileşen yedek path'te en az 250 yeşil piksel çizmek zorunda; açıkça verilen mavi

material yeşil temayı ezmeli. Tasarımı iki ImageRenderer tuhaflığı belirledi: macOS 27'de hiçbir

şey çizmeyen bir görünüm önceki render'ı tutan bir buffer döndürebiliyor, yani yalnız şeffaflık

güvenilir bir sinyal değil; metin alanları da kırmızı-sarı bir placeholder olarak çiziliyor, bu

yüzden kırmızı test tint'i olarak kullanılamadı. Değişiklikten sonra commit'te listelenen 11

kaynak mutasyonunun her biri en az bir testi kırıyor.

Native path'te de aynı kör nokta vardı. OS 26'da render testi yalnız bir görüntü dönmesini

bekliyordu. Native view her zaman yedeğini çizecek şekilde değiştirildiğinde 39 testin hepsi yine

geçti. ImageRenderer blur'u yakalamadığı ve native glass'ı OS'a göre farklı çizdiği için tint'e

bakan bir kontrol her yerde çalışamazdı. 29ce746 commit'i bunun yerine her bileşeni iki path'te

render ediyor: aynı görünümün iki yedek render'ı piksel piksel aynı, native render ise yedekten

250'den fazla pikselde farklı olmalı. Commit, bileşen başına macOS 27'de en az 3.857, iOS 26.5 ve

27 simülatörlerinde en az 5.005 farklı piksel ölçüldüğünü kaydediyor.

2.1.0 sırasında bir sorun daha çıktı. Piksel eşitliği testleri kabaca dört tam koşudan birinde

kırılıyordu; biri de swift test --no-parallel ile her seferinde kırılıyordu. 68f94e8 commit'i

iki neden buldu: ImageRenderer görüntüsünü önbelleğe alıyor ve bir görünümün ilk render'ları,

Material kenarları ve glyph'ler oturana kadar sonrakilerden farklı çıkıyor. Test yardımcısı artık

her geçişte yeni bir renderer kullanıyor, sekiz geçişlik bir ısınma yapıyor ve ardışık iki geçiş

eşleşince duruyor; üst sınır 16 geçiş.

Kullanım

Paketi Xcode'da File › Add Package Dependencies… menüsünden

https://github.com/muhittincamdali/LiquidGlassKit.git adresiyle ya da Package.swift üzerinden

ekleyin. README'deki hızlı başlangıç örneği, test target'ında birebir derleniyor:

swift
1import LiquidGlassKit
2import SwiftUI
3 
4struct ContentView: View {
5 var body: some View {
6 ZStack {
7 LinearGradient(colors: [.indigo, .pink], startPoint: .top, endPoint: .bottom)
8 .ignoresSafeArea()
9 VStack(spacing: 16) {
10 GlassCard {
11 Label("Hello, glass", systemImage: "sparkles")
12 }
13 GlassButton("Tap me", systemImage: "hand.tap") {}
14 }
15 }
16 }
17}

Herhangi bir görünüm tek modifier'la glass üzerine oturuyor:

swift
1import LiquidGlassKit
2import SwiftUI
3 
4struct NowPlayingBadge: View {
5 var body: some View {
6 Label("Now Playing", systemImage: "music.note")
7 .padding()
8 .glassSurface(.tinted(.purple), in: .capsule)
9 }
10}

Tema ve rendering mode bütün hiyerarşiye uygulanıyor:

swift
1struct ThemedApp: View {
2 var body: some View {
3 RootView()
4 .glassTheme(GlassTheme(material: .tinted(.indigo), shape: .roundedRectangle(cornerRadius: 12)))
5 .glassRenderingMode(.automatic) // or .fallback to use the Material look everywhere
6 }
7}

Hazır bir tema ayarlanıp paketi kullanan Swift koduna dönüştürülebiliyor:

swift
1var theme = GlassPreset.named("Ocean")?.theme ?? .standard
2theme.shadowRadius = 4
3let source = GlassCodeExporter().viewCode(named: "PromoCard", theme: theme)

Examples/LiquidGlassDemo içindeki demo uygulaması bir galeri, tab bar'lar, sheet, native ile

yedek path karşılaştırması ve bir temayı ayarlayıp dışa aktarılan kodunu gösteren bir playground

ekranı içeriyor. DocC makaleleri GitHub Pages'te yayında.

Sınırlar

  • Son glass görünümü test edilmiyor. ImageRenderer blur'u yakalamıyor, native glass'ı da

ancak kısmen yakalıyor. Testler yedek path'in tint'ini ve kapladığı alanı, native path'in yedekten

farklı çizdiğini ölçüyor; README bunu açıkça yazıyor.

  • Cihaz kapsamı. CI suite'i macOS'ta, bir iOS 26 simülatöründe ve yalnız yedek path'in olduğu

bir iOS 18.6 simülatöründe koşturuyor. iOS 17 job'ı ve fiziksel cihazda koşum yok.

  • Performans ölçümü yok. Repoda yedek blur ve gölgeler için benchmark ya da profil sonucu

bulunmuyor.

  • visionOS yalnız yedek path'i kullanıyor, çünkü SwiftUI orada glassEffect sunmuyor.
  • GlassSheet bir overlay, sistem sheet'i değil: detent yok, ayrı bir presentation context

yok ve tvOS'ta kullanılamıyor. GlassSlider da tvOS'ta yok.

  • Sabit şekiller. GlassShape dört case'ten oluşuyor; özel şekil desteklenmiyor.
  • Yerelleştirme. GlassButton, GlassTab ve GlassTextField başlıkları String alıyor ve

olduğu gibi gösteriyor; LocalizedStringKey araması yapılmıyor ve paketin kendi yerelleştirilmiş

metinleri yok.

  • Toolchain. Native path OS 26 SDK'sına karşı derlendiği için eski deployment target'larda da

Xcode 26 veya üstü gerekiyor.

Güncel sürüm 2.1.0; README'ye göre sonraki bir sürüm henüz planlanmış değil.

Kaynak kod: github.com/muhittincamdali/LiquidGlassKit

Etiketler

#swiftui#liquid-glass#swift-package#testing#accessibility
Muhittin Çamdalı

Muhittin Çamdalı

Senior Mobile Engineer

12+ yıllık deneyime sahip Senior Mobile Engineer. iOS, Flutter ve React Native ile mobil ürünler geliştiriyorum; full-stack ve AI-native çalışıyorum.

iOS Geliştirme Haberleri

Haftalık Swift tips, SwiftUI tricks ve iOS best practices. Spam yok, sadece değerli içerik.

Onay e-postasındaki bağlantıyı açıp “Aboneliğimi onayla” düğmesine bastığında aboneliğin başlar. Bültende açılma/tıklama istatistikleri tutulur; dilediğin an tek tıkla ayrılabilirsin. Gizlilik

Paylaş

İlgili İçerik