Czym jest Liquid Glass i dlaczego zmienia wszystko
Jeśli śledzisz ekosystem Apple, pewnie już wiesz — iOS 26 to nie była zwykła aktualizacja. Na WWDC 2025 Apple pokazał największą rewolucję wizualną od czasów iOS 7 i nazwał ją Liquid Glass. To nowy, adaptacyjny materiał dla kontrolek i elementów nawigacyjnych, łączący optyczne właściwości szkła z płynnością cieczy.
Brzmi poetycko? Bo tak trochę jest. W praktyce oznacza to dynamiczne odbicia, załamanie światła w czasie rzeczywistym i efekty, które reagują na dotyk. Szczerze mówiąc, kiedy pierwszy raz zobaczyłem to na żywo, byłem pod wrażeniem.
Ale Liquid Glass to nie tylko kwestia estetyki. Apple dał nam kompletny zestaw API w SwiftUI, który pozwala dodać efekt szkła do własnych widoków za pomocą dosłownie jednego modyfikatora. W tym artykule pokażę krok po kroku, jak korzystać z każdego elementu tego systemu — od prostego .glassEffect(), przez GlassEffectContainer, aż po zaawansowane przejścia morficzne z glassEffectID.
No to zaczynamy.
Filozofia projektowa — kiedy używać Liquid Glass
Zanim przejdziemy do kodu, musisz zrozumieć jedną fundamentalną zasadę: Liquid Glass jest przeznaczony wyłącznie dla warstwy nawigacyjnej, tej która unosi się nad treścią aplikacji. Nigdy nie stosuj go do samej treści — list, tabel, multimediów czy gęstych układów tekstowych.
Apple jasno komunikuje tę hierarchię:
- Stosuj Liquid Glass do: przycisków akcji, toolbarów, pływających kart, menu kontekstowych, pływających paneli
- Nie stosuj do: wierszy list, pełnoekranowych powierzchni, treści głównej, gęstych widoków z dużą ilością tekstu
Dlaczego? Bo szkło nie może próbkować innego szkła. Jeśli pokryjesz wszystkie elementy UI efektem szklenia, rezultat będzie wizualnie chaotyczny i zniszczy hierarchię, zamiast ją budować.
Liquid Glass ma podkreślać nawigację i kontrolki — nie zastępować całego interfejsu. To dosyć ważne rozróżnienie, o którym łatwo zapomnieć, gdy człowiek się wciągnie w zabawę nowym API.
Modyfikator glassEffect() — od zera do bohatera
Dodanie efektu Liquid Glass do widoku SwiftUI jest zaskakująco proste. Cała magia kryje się w jednym modyfikatorze:
import SwiftUI
struct BasicGlassView: View {
var body: some View {
Text("Witaj, Liquid Glass!")
.padding()
.glassEffect()
}
}
Tak, to naprawdę tyle. Domyślnie .glassEffect() nakłada wariant .regular w kształcie kapsułki (Capsule) za treścią widoku. SwiftUI automatycznie zastosuje wibracyjny kolor tekstu, który adaptuje się do tła — dzięki temu tekst pozostaje czytelny niezależnie od tego, co jest pod spodem.
Pełna sygnatura modyfikatora
Warto znać pełną definicję API, żeby wiedzieć, co mamy do dyspozycji:
func glassEffect<S: Shape>(
_ glass: Glass = .regular,
in shape: S = DefaultGlassEffectShape,
isEnabled: Bool = true
) -> some View
Trzy parametry, trzy wymiary konfiguracji: typ szkła, kształt i możliwość warunkowego włączenia efektu. Przejdźmy przez każdy z nich po kolei.
Typy szkła — regular, clear i identity
Struktura Glass oferuje trzy statyczne warianty. Każdy ma swoje konkretne zastosowanie:
Glass.regular— domyślny, adaptacyjny wariant. Najbardziej powszechny w systemie. Daje wyraźny efekt szkła z odpowiednim poziomem rozmycia tła.Glass.clear— wariant o wysokiej przezroczystości. Subtelniejszy, świetny do elementów drugorzędnych, gdzie nie chcesz odciągać uwagi od głównej treści.Glass.identity— brak efektu. Brzmi dziwnie, ale jest przydatny, gdy chcesz warunkowo wyłączyć efekt szkła bez przebudowywania hierarchii widoków.
// Porównanie typów szkła
VStack(spacing: 20) {
Text("Regular")
.padding()
.glassEffect(.regular)
Text("Clear")
.padding()
.glassEffect(.clear)
Text("Identity (brak efektu)")
.padding()
.glassEffect(.identity)
}
Własne kształty efektu
Domyślna kapsułka nie zawsze pasuje do Twojego designu — i to jest w porządku. Możesz użyć dowolnego kształtu zgodnego z protokołem Shape:
// Zaokrąglony prostokąt
Text("Zaokrąglone szkło")
.font(.title2)
.padding()
.glassEffect(in: .rect(cornerRadius: 16.0))
// Koło
Image(systemName: "star.fill")
.font(.title)
.frame(width: 60, height: 60)
.glassEffect(in: .circle)
// Elipsa
Text("Eliptyczne szkło")
.padding(.horizontal, 30)
.padding(.vertical, 10)
.glassEffect(in: .ellipse)
Kolorowanie szkła — tint, ale z umiarem
Liquid Glass można kolorować za pomocą metody .tint() na obiekcie Glass. Apple podkreśla jednak (i słusznie), że tintowanie powinno przekazywać znaczenie — nie służyć jedynie dekoracji. Używaj go do wyróżniania głównych akcji lub komunikowania stanu:
// Przycisk głównej akcji — zaakcentowany kolorem
Button("Zapisz") { }
.padding()
.glassEffect(.regular.tint(.blue))
// Przycisk destrukcyjnej akcji
Button("Usuń") { }
.padding()
.glassEffect(.regular.tint(.red))
// Subtelny tint z przezroczystością
Text("Status: Aktywny")
.padding()
.glassEffect(.regular.tint(.green.opacity(0.6)))
Tint używa wibracyjnego koloru, który automatycznie adaptuje się do treści pod spodem. Dzięki temu kolorowe szkło dobrze wygląda niezależnie od tła — nie musisz się martwić o kontrast.
Tryb interaktywny — szkło reagujące na dotyk
To chyba moja ulubiona część. Modyfikator .interactive() włącza responsywne zachowanie — szkło reaguje na dotyk skalowaniem, sprężystym odbiciem i delikatnym połyskiem:
// Interaktywny przycisk ze szkłem
Button(action: { print("Dotknięto!") }) {
Label("Udostępnij", systemImage: "square.and.arrow.up")
.padding()
}
.glassEffect(.regular.interactive())
// Kombinacja — tint + interaktywność
Button("Kup teraz") { }
.padding()
.glassEffect(.regular.tint(.orange).interactive())
Ważna uwaga: włączaj .interactive() tylko na elementach dotykalnych — przyciskach, kontrolkach, interaktywnych kontenerach. Na statycznych etykietach i gęstych listach lepiej tego unikać, bo zwiększa obciążenie GPU i może rozpraszać uwagę od głównych akcji.
GlassEffectContainer — grupowanie elementów szklanych
Gdy masz kilka elementów z efektem szkła blisko siebie, powinieneś umieścić je w GlassEffectContainer. To kontener widoku wprowadzony w iOS 26, który łączy wiele kształtów Liquid Glass w jedną wspólną formę i umożliwia morfowanie pomiędzy nimi.
Dlaczego to takie ważne? Bo (jak już wspomniałem) szkło nie może próbkować innego szkła. Bez kontenera elementy szklane umieszczone obok siebie mogą wyglądać po prostu źle — pojawiają się artefakty wizualne. GlassEffectContainer rozwiązuje ten problem, tworząc wspólny region próbkowania.
GlassEffectContainer(spacing: 30) {
HStack(spacing: 20) {
Button(action: {}) {
Image(systemName: "house.fill")
.frame(width: 44, height: 44)
}
.glassEffect()
Button(action: {}) {
Image(systemName: "gear")
.frame(width: 44, height: 44)
}
.glassEffect()
Button(action: {}) {
Image(systemName: "person.fill")
.frame(width: 44, height: 44)
}
.glassEffect()
}
.padding()
}
Parametr spacing kontroluje próg morfowania — określa, jak blisko siebie muszą być elementy, zanim wizualnie złączą się w jedną formę szklaną. To sprytne rozwiązanie, bo elementy w zasięgu tej odległości automatycznie łączą się w jeden kształt.
Przejścia morficzne z glassEffectID
Okej, tu zaczyna się prawdziwa magia. Modyfikator .glassEffectID(_:in:) pozwala tworzyć płynne przejścia morficzne pomiędzy elementami szklanymi. Zamiast prostego fade-in/fade-out, kształty szkła płynnie przechodzą jeden w drugi.
Do działania potrzebujesz przestrzeni nazw (@Namespace), która koordynuje identyfikatory w obrębie GlassEffectContainer:
struct ExpandableMenu: View {
@State private var isExpanded = false
@Namespace private var glassNamespace
var body: some View {
ZStack {
// Tło — gradient, aby efekt szkła był widoczny
LinearGradient(
colors: [.indigo, .purple, .pink],
startPoint: .topLeading,
endPoint: .bottomTrailing
)
.ignoresSafeArea()
GlassEffectContainer(spacing: 20) {
VStack(spacing: 15) {
// Główny przycisk — zawsze widoczny
Button {
withAnimation(.bouncy) {
isExpanded.toggle()
}
} label: {
Image(systemName: isExpanded ? "xmark" : "plus")
.font(.title2)
.frame(width: 56, height: 56)
}
.glassEffect(.regular.tint(.blue).interactive())
.glassEffectID("main", in: glassNamespace)
// Dodatkowe przyciski — pojawiają się po rozwinięciu
if isExpanded {
Button {
// Akcja aparatu
} label: {
Label("Aparat", systemImage: "camera")
.padding(.horizontal)
}
.glassEffect(.regular.interactive())
.glassEffectID("camera", in: glassNamespace)
Button {
// Akcja zdjęć
} label: {
Label("Zdjęcia", systemImage: "photo.on.rectangle")
.padding(.horizontal)
}
.glassEffect(.regular.interactive())
.glassEffectID("photos", in: glassNamespace)
Button {
// Akcja plików
} label: {
Label("Pliki", systemImage: "folder")
.padding(.horizontal)
}
.glassEffect(.regular.interactive())
.glassEffectID("files", in: glassNamespace)
}
}
}
}
}
}
Dzięki glassEffectID i animacji .bouncy, gdy użytkownik kliknie przycisk „+", dodatkowe opcje nie pojawią się z prostym fade — morficznie wyłonią się z głównego przycisku. Efekt jest naprawdę imponujący i sprawia, że interfejs wydaje się żywy.
Style przycisków dla Liquid Glass
iOS 26 przynosi dedykowane style przycisków zoptymalizowane pod efekt szkła. To chyba najprostszy sposób na integrację Liquid Glass z przyciskami — bez ręcznego kombinowania z modyfikatorami:
VStack(spacing: 20) {
// Przycisk translucent — do akcji drugorzędnych
Button("Anuluj") {
// Anulowanie operacji
}
.buttonStyle(.glass)
// Przycisk prominent — do akcji głównych
Button("Potwierdź zakup") {
// Potwierdzenie
}
.buttonStyle(.glassProminent)
}
Zasada jest prosta: .glass do akcji drugorzędnych, .glassProminent do akcji głównych. Oba style automatycznie reagują na kontekst — adaptują kolor, rozmycie i intensywność do otaczającej treści. Nie musisz niczego konfigurować ręcznie.
Sheety z Liquid Glass w iOS 26
Częściowe arkusze (sheets) w iOS 26 automatycznie zyskują wygląd Liquid Glass — wyglądają, jakby unosiły się nad interfejsem. Ale uwaga: jeśli wcześniej używałeś .presentationBackground() do dostosowywania tła sheetów, powinieneś to usunąć:
struct SheetExample: View {
@State private var showSheet = false
var body: some View {
Button("Pokaż arkusz") {
showSheet = true
}
.sheet(isPresented: $showSheet) {
VStack {
Text("To jest sheet z Liquid Glass")
.font(.headline)
Text("System automatycznie nakłada efekt szkła")
.foregroundStyle(.secondary)
}
.presentationDetents([.medium, .large])
// NIE dodawaj .presentationBackground() w iOS 26!
// Pozwól systemowi samodzielnie zastosować Liquid Glass
}
}
}
Pozwól systemowi samodzielnie zastosować efekt. Poważnie — usunięcie jednej linii kodu daje lepszy rezultat niż ręczna konfiguracja. Rzadko się to zdarza w programowaniu.
Migracja istniejącej aplikacji
Dobra wiadomość: migracja do Liquid Glass jest w dużej mierze automatyczna. Niezależnie od tego, czy Twoja aplikacja używa SwiftUI, UIKit czy AppKit — systemowe komponenty automatycznie zyskają nowy wygląd po rekompilacji z Xcode 26.
Są jednak rzeczy, na które warto zwrócić uwagę:
- Usuń niestandardowe tła — arkusze i toolbary powinny korzystać z automatycznego szkła
- Sprawdź ikony SF Symbols — dla Liquid Glass rozważ warianty „none" zamiast wypełnionych w kółku
- Testuj z różnymi tłami — efekt szkła wygląda inaczej w zależności od treści pod spodem (i to potrafi zaskoczyć)
- Zaudytuj przepływ aplikacji — zidentyfikuj widoki wymagające ręcznych zmian
Kompatybilność wsteczna — obsługa starszych wersji iOS
API Liquid Glass jest dostępne wyłącznie od iOS 26. Jeśli Twoja aplikacja wspiera starsze wersje systemu (a pewnie tak), musisz warunkowo stosować efekty szkła. Oto rozszerzenie, które sprawdza się naprawdę dobrze:
extension View {
@ViewBuilder
func adaptiveGlassEffect(
in shape: some Shape = Capsule(),
interactive: Bool = false
) -> some View {
if #available(iOS 26.0, *) {
let glass = interactive
? Glass.regular.interactive()
: .regular
self.glassEffect(glass, in: shape)
} else {
self
.background(
shape
.fill(.ultraThinMaterial)
.overlay(
LinearGradient(
colors: [
.white.opacity(0.3),
.clear
],
startPoint: .topLeading,
endPoint: .bottomTrailing
)
)
.overlay(
shape.stroke(
.white.opacity(0.2),
lineWidth: 1
)
)
)
}
}
}
Dzięki temu rozszerzeniu możesz wszędzie używać .adaptiveGlassEffect() — na iOS 26 dostaniesz natywne Liquid Glass, a na starszych wersjach materiał .ultraThinMaterial z gradientem imitującym efekt. Nie jest to identyczne, ale użytkownicy i tak nie będą porównywać obu wersji jednocześnie.
Znane pułapki i ograniczenia
Zanim rzucisz się w wir implementacji, warto znać kilka pułapek. Część z nich to problemy zgłaszane przez deweloperów, a część to po prostu specyfika nowego API:
- Menu w GlassEffectContainer — w iOS 26.1 umieszczenie komponentu
MenuwGlassEffectContainerpsuje animację morficzną. Na razie lepiej tego unikać. - Glitche morfowania — nawet przy poprawnym użyciu API, animacje morficzne potrafią czasem migać lub zachowywać się niespodziewanie. To znany problem platformy i Apple prawdopodobnie go naprawi.
- Wydajność na starszych urządzeniach — efekty szkła wymagają dodatkowych zasobów GPU. Na starszych modelach iPhone warto rozważyć warunkowe wyłączanie efektów.
- Dostępność — Apple dodał kontrolki do redukcji przezroczystości Liquid Glass. Twoja aplikacja automatycznie respektuje te ustawienia, ale dobrze jest przetestować, jak wygląda interfejs z ustawieniami dostępności włączonymi.
Często zadawane pytania
Czy muszę zmienić kod, żeby moja aplikacja używała Liquid Glass?
Nie — dla systemowych komponentów (toolbary, TabView, sheety, NavigationBar) wystarczy rekompilacja z Xcode 26. Liquid Glass zostanie zastosowany automatycznie. Ręczne użycie .glassEffect() jest potrzebne tylko do własnych, niestandardowych widoków.
Czy Liquid Glass działa z UIKit i AppKit?
Tak. Systemowe kontrolki automatycznie zyskują nowy wygląd po rekompilacji. Dla niestandardowych widoków UIKit jest UIGlassEffect i powiązane API. W SwiftUI modyfikator .glassEffect() to główny punkt wejścia.
A co z macOS i watchOS?
Liquid Glass to zunifikowany język designu obejmujący iOS 26, iPadOS 26, macOS Tahoe 26, watchOS 26, tvOS 26 i visionOS 26. API .glassEffect() jest dostępne na wszystkich tych platformach, choć zachowanie wizualne może się nieznacznie różnić w zależności od urządzenia.
Jaka jest różnica między .glass a .glassProminent?
.glass tworzy częściowo przezroczysty przycisk szklany — idealny do akcji drugorzędnych. .glassProminent jest bardziej opaque i wyrazisty, przeznaczony do akcji głównych. W skrócie: mniej ważne — .glass, bardziej ważne — .glassProminent.
Jak poprawić wydajność efektów Liquid Glass?
Kilka sprawdzonych strategii: używaj GlassEffectContainer do grupowania elementów (poprawia wydajność renderowania), unikaj .interactive() na wielu elementach jednocześnie, ogranicz efekty szkła do warstwy nawigacyjnej. Na starszych urządzeniach możesz warunkowe wyłączyć efekty parametrem isEnabled w .glassEffect().