Liquid Glass trong SwiftUI: Hướng Dẫn Toàn Diện cho iOS 26

Hướng dẫn chi tiết Liquid Glass trong SwiftUI trên iOS 26 — từ glassEffect() cơ bản, GlassEffectContainer, morphing transitions, cho đến best practices và ví dụ Floating Action Toolbar hoàn chỉnh.

Giới Thiệu: Kỷ Nguyên Mới của Thiết Kế iOS

Tại WWDC 2025, Apple đã chính thức giới thiệu Liquid Glass — và thành thật mà nói, đây không chỉ là một hiệu ứng thị giác mới. Nó là cả một triết lý thiết kế hoàn toàn khác biệt, thay đổi cách các thành phần giao diện tương tác với nội dung phía sau chúng. Với iOS 26, SwiftUI mang đến bộ API mạnh mẽ để bạn tích hợp Liquid Glass vào ứng dụng một cách nhanh chóng.

Nếu bạn đã từng làm việc với các hiệu ứng blur truyền thống như .ultraThinMaterial hay .regularMaterial, thì Liquid Glass sẽ khiến bạn bất ngờ. Thay vì chỉ đơn giản làm mờ nội dung phía sau, Liquid Glass sử dụng kỹ thuật lensing (khúc xạ ánh sáng) để bẻ cong và tập trung ánh sáng theo thời gian thực. Cảm giác khi dùng nó giống như đang nhìn qua một lớp kính trong suốt thật sự — rất sống động và phản hồi tức thì.

Trong bài viết này, chúng ta sẽ đi từ những khái niệm cốt lõi, API cơ bản, cho đến các kỹ thuật nâng cao như morphing transitions và accessibility. Cuối bài, mình sẽ cùng bạn xây dựng một ví dụ thực tế hoàn chỉnh để bạn có thể áp dụng ngay.

Các Đặc Tính Cốt Lõi của Liquid Glass

Trước khi bắt tay vào code, hãy nắm vững năm đặc tính cốt lõi của Liquid Glass. Hiểu rõ phần này sẽ giúp bạn sử dụng API hiệu quả hơn rất nhiều.

1. Lensing (Khúc xạ ánh sáng)

Đây là đặc tính nền tảng nhất. Thay vì dùng blur truyền thống, Liquid Glass bẻ cong và tập trung ánh sáng theo thời gian thực. Hiệu ứng giống như nhìn qua một lớp kính trong suốt — nội dung phía sau bị biến dạng nhẹ theo dạng thấu kính, mang lại chiều sâu đáng kinh ngạc cho giao diện.

2. Materialization (Vật chất hóa)

Khi một thành phần Liquid Glass xuất hiện trên màn hình, nó không đơn giản pop-up hay fade-in. Phần tử dần dần điều chỉnh mức độ bẻ cong ánh sáng, tạo cảm giác như vật liệu đang hình thành từ hư không. Nói thật, hiệu ứng này nhìn rất "phê" khi thấy lần đầu.

3. Fluidity (Tính linh hoạt)

Liquid Glass có tính linh hoạt giống như gel, phản hồi tức thì với mọi tương tác chạm. Khi bạn nhấn vào một thành phần glass, nó co giãn và biến dạng tự nhiên — mà bạn không cần viết thêm dòng animation code nào cả.

4. Morphing (Biến hình)

Đặc tính morphing cho phép các thành phần Liquid Glass chuyển đổi linh hoạt giữa các trạng thái. Một nút bấm đơn lẻ có thể mở rộng thành thanh công cụ nhiều nút, với hiệu ứng chuyển tiếp mượt mà. Đây là một trong những khả năng ấn tượng nhất mà chúng ta sẽ khám phá chi tiết ở phần sau.

5. Adaptivity (Tính thích ứng)

Liquid Glass tự động điều chỉnh theo nội dung, chế độ màu (light/dark), và kích thước của thành phần. Hệ thống multi-layer composition đảm bảo nhiều lớp glass hoạt động hài hòa với nhau trong mọi ngữ cảnh.

Bắt Đầu với glassEffect() Cơ Bản

OK, giờ vào phần thú vị nhất — code! API cơ bản nhất để áp dụng Liquid Glass là modifier .glassEffect():

Text("Hello, Liquid Glass!")
    .padding()
    .glassEffect() // Mặc định: .regular, hình dạng .capsule

Chỉ một dòng code duy nhất. Vậy thôi. Bạn đã có thể biến bất kỳ view nào thành một thành phần Liquid Glass hoàn chỉnh với variant .regular và hình dạng .capsule.

Khi cần kiểm soát chi tiết hơn, bạn có thể truyền thêm tham số:

Text("Custom Glass")
    .padding()
    .glassEffect(.regular, in: .capsule, isEnabled: true)

Chữ ký đầy đủ của hàm như sau:

func glassEffect<S: Shape>(
    _ glass: Glass = .regular,
    in shape: S = DefaultGlassEffectShape,
    isEnabled: Bool = true
) -> some View

Trong đó:

  • glass: Loại vật liệu glass cần áp dụng (mặc định là .regular)
  • shape: Hình dạng của hiệu ứng, chấp nhận bất kỳ kiểu nào tuân thủ protocol Shape
  • isEnabled: Bật/tắt hiệu ứng một cách có điều kiện

Tham số isEnabled rất hữu ích khi bạn muốn toggle hiệu ứng glass dựa trên trạng thái ứng dụng:

@State private var useGlass = true

Text("Toggle Glass")
    .padding()
    .glassEffect(.regular, in: .capsule, isEnabled: useGlass)

Các Biến Thể Vật Liệu (Material Variants)

Liquid Glass cung cấp ba biến thể vật liệu chính. Mỗi biến thể phục vụ một mục đích riêng, nên chọn đúng cái sẽ giúp giao diện của bạn trông chuyên nghiệp hơn hẳn.

.regular — Biến thể mặc định

Đây là biến thể phổ biến nhất, phù hợp cho hầu hết các thành phần UI. Nó cung cấp mức độ trong suốt vừa phải — nội dung phía sau hiển thị nhẹ nhàng qua lớp glass mà vẫn đảm bảo khả năng đọc.

Text("Regular Glass")
    .padding()
    .glassEffect(.regular)

.clear — Biến thể trong suốt cao

Biến thể .clear trong suốt hơn đáng kể so với .regular. Nó được thiết kế cho các ngữ cảnh có nền giàu media như hình ảnh hoặc video. Khi dùng .clear, bạn cần lưu ý:

  • Phần tử phải nằm trên nội dung media phong phú
  • Nội dung phía dưới không bị ảnh hưởng bởi hiệu ứng dimming
  • Nội dung phía trên glass phải đậm và sáng rõ ràng
Image(systemName: "play.fill")
    .font(.title)
    .foregroundStyle(.white)
    .frame(width: 60, height: 60)
    .glassEffect(.clear)

.identity — Vô hiệu hóa hiệu ứng

Biến thể .identity thực chất không áp dụng bất kỳ hiệu ứng glass nào. Nghe hơi lạ, nhưng nó cực kỳ hữu ích khi bạn cần tắt hiệu ứng có điều kiện — đặc biệt cho accessibility:

@Environment(\.accessibilityReduceTransparency) var reduceTransparency

Text("Adaptive Glass")
    .padding()
    .glassEffect(reduceTransparency ? .identity : .regular)

Tinting và Interactive Modifiers

Liquid Glass không dừng lại ở ba biến thể cơ bản. Apple còn cung cấp các modifier bổ sung để bạn tùy chỉnh giao diện và hành vi tương tác.

Tinting (Tô màu)

Modifier .tint() cho phép thêm một lớp màu phủ lên glass:

Text("Tinted Glass")
    .padding()
    .glassEffect(.regular.tint(.blue))

Bạn cũng có thể kiểm soát cường độ tint bằng cách điều chỉnh opacity:

Text("Subtle Tint")
    .padding()
    .glassEffect(.regular.tint(.purple.opacity(0.6)))

Lưu ý quan trọng: Apple khuyến nghị chỉ sử dụng tinting cho các hành động chính (primary actions), không phải để trang trí. Lạm dụng tint sẽ làm giảm hiệu quả thị giác và gây rối cho người dùng — mình đã thấy không ít app mắc lỗi này.

Interactive Modifier

Trên iOS, modifier .interactive() kích hoạt hàng loạt hành vi phản hồi tương tác:

Button("Tap Me") { }
    .glassEffect(.regular.interactive())

Khi một thành phần glass được đánh dấu interactive, nó tự động có:

  • Scaling on press: Thu nhỏ nhẹ khi nhấn xuống, tạo cảm giác vật lý
  • Bouncing animation: Bật nẩy mượt mà khi thả ra
  • Shimmering effect: Lấp lánh tinh tế trên bề mặt
  • Touch-point illumination: Điểm chạm phát sáng và tỏa ra các thành phần lân cận

Tất cả đều tự động — bạn không cần viết animation code gì cả. Khá tiện, phải không?

Kết Hợp Nhiều Modifiers (Method Chaining)

Các modifier Liquid Glass hỗ trợ method chaining, nên bạn có thể kết hợp thoải mái:

// Kết hợp tint và interactive
Button("Primary Action") { }
    .glassEffect(.regular.tint(.orange).interactive())

// Thứ tự không quan trọng
Button("Another Action") { }
    .glassEffect(.clear.interactive().tint(.blue))

Tùy Chỉnh Hình Dạng (Custom Shapes)

Một điểm mạnh của API Liquid Glass là khả năng tùy chỉnh hình dạng linh hoạt. Tham số in: chấp nhận bất kỳ kiểu nào tuân thủ protocol Shape:

// Hình viên nang (mặc định)
Text("Capsule")
    .padding()
    .glassEffect(.regular, in: .capsule)

// Hình tròn
Image(systemName: "plus")
    .frame(width: 50, height: 50)
    .glassEffect(.regular, in: .circle)

// Hình chữ nhật bo góc tùy chỉnh
Text("Rounded Rectangle")
    .padding()
    .glassEffect(.regular, in: RoundedRectangle(cornerRadius: 16))

// Bo góc đồng tâm với container
Text("Container Concentric")
    .padding()
    .glassEffect(.regular, in: .rect(cornerRadius: .containerConcentric))

Giá trị .containerConcentric đặc biệt hay khi bạn muốn bo góc của thành phần glass tự động đồng tâm với container cha — tạo ra khoảng cách đều đặn giữa các lớp bo góc mà không cần tính toán thủ công.

Button Styles với Liquid Glass

SwiftUI trong iOS 26 giới thiệu hai button style mới dành riêng cho Liquid Glass. Đây có lẽ là cách nhanh nhất để bắt đầu.

.glass — Nút bấm trong suốt

Style .glass tạo nút bấm glass trong suốt, phù hợp cho hành động phụ (secondary actions):

Button("Secondary Action") {
    // Xử lý sự kiện
}
.buttonStyle(.glass)

.glassProminent — Nút bấm nổi bật

Style .glassProminent tạo nút bấm glass đục hơn, nổi bật hơn, dành cho hành động chính (primary actions):

Button("Primary Action") {
    // Xử lý sự kiện
}
.buttonStyle(.glassProminent)

Hai style này giúp bạn xây dựng hệ thống phân cấp hành động (action hierarchy) rõ ràng mà không cần tùy chỉnh thủ công. Ví dụ thực tế:

HStack(spacing: 16) {
    Button("Cancel") {
        // Hủy bỏ
    }
    .buttonStyle(.glass)

    Button("Save") {
        // Lưu
    }
    .buttonStyle(.glassProminent)
}

Text, Icons và Labels với Liquid Glass

Liquid Glass hoạt động rất tốt với mọi loại nội dung trong SwiftUI. Dưới đây là một số pattern phổ biến mà bạn sẽ dùng thường xuyên.

Text với Glass

Text("Glass Text")
    .font(.title)
    .bold()
    .foregroundStyle(.white)
    .padding()
    .glassEffect()

Khi dùng text trên nền glass, hãy ưu tiên màu .white hoặc các màu sáng, đậm để đảm bảo đọc được trên nền trong suốt.

Icons với Glass

Image(systemName: "heart.fill")
    .font(.largeTitle)
    .foregroundStyle(.white)
    .frame(width: 60, height: 60)
    .glassEffect(.regular.interactive())

SF Symbols kết hợp với Liquid Glass tạo ra icon button rất đẹp. Một tip nhỏ: hãy đặt .frame() trước .glassEffect() để kiểm soát kích thước vùng glass.

Labels với Glass

Label("Settings", systemImage: "gear")
    .labelStyle(.iconOnly)
    .padding()
    .glassEffect()

Pattern .labelStyle(.iconOnly) kết hợp glass rất phù hợp cho toolbar hoặc tab bar, nơi biểu tượng đã đủ truyền đạt ý nghĩa.

GlassEffectContainer: Nhóm Các Thành Phần Glass

GlassEffectContainer là một container view đặc biệt mà bạn sẽ dùng rất nhiều khi làm việc với Liquid Glass. Nó giải quyết một số vấn đề quan trọng khi có nhiều thành phần glass cùng tồn tại.

Tại sao cần GlassEffectContainer?

Ba lý do chính:

  1. Nhóm nhiều hình dạng glass: Gộp các thành phần liên quan thành một đơn vị thị giác thống nhất
  2. Cải thiện hiệu suất: Hệ thống tối ưu hóa rendering khi các thành phần glass nằm trong cùng container
  3. Kích hoạt morphing: Cho phép các thành phần glass chuyển đổi mượt mà giữa các trạng thái

Quy tắc quan trọng: Glass không thể sample glass khác. Nghĩa là nếu bạn đặt glass lên trên glass mà không dùng GlassEffectContainer, hiệu ứng sẽ bị lỗi. Container giải quyết vấn đề này bằng cách xử lý tất cả glass con như một nhóm thống nhất.

Sử dụng cơ bản

GlassEffectContainer {
    HStack(spacing: 20) {
        Image(systemName: "pencil")
            .frame(width: 44, height: 44)
            .glassEffect(.regular.interactive())

        Image(systemName: "eraser")
            .frame(width: 44, height: 44)
            .glassEffect(.regular.interactive())
    }
}

Tùy chỉnh khoảng cách

Tham số spacing kiểm soát khoảng cách giữa các thành phần glass con, ảnh hưởng đến cách chúng tương tác thị giác:

GlassEffectContainer(spacing: 40.0) {
    ForEach(icons, id: \.self) { icon in
        Image(systemName: icon)
            .frame(width: 44, height: 44)
            .glassEffect()
    }
}

Morphing Transitions với glassEffectID

Đây là phần mình thấy hấp dẫn nhất. Morphing transitions cho phép các thành phần glass biến hình mượt mà giữa các trạng thái — và cách triển khai lại cực kỳ đơn giản.

Cách hoạt động

Modifier .glassEffectID(_:in:) gán một định danh duy nhất cho mỗi thành phần glass trong một namespace. Khi trạng thái thay đổi, hệ thống tự động tạo hiệu ứng morphing — miễn là các thành phần nằm trong cùng một GlassEffectContainer.

Ví dụ chi tiết

struct MorphingExample: View {
    @State private var isExpanded = false
    @Namespace private var namespace

    var body: some View {
        GlassEffectContainer(spacing: 30) {
            Button(isExpanded ? "Collapse" : "Expand") {
                withAnimation(.bouncy) {
                    isExpanded.toggle()
                }
            }
            .glassEffect()
            .glassEffectID("toggle", in: namespace)

            if isExpanded {
                Button("Action 1") { }
                    .glassEffect()
                    .glassEffectID("action1", in: namespace)

                Button("Action 2") { }
                    .glassEffect()
                    .glassEffectID("action2", in: namespace)
            }
        }
    }
}

Hãy phân tích đoạn code trên:

  • @Namespace private var namespace tạo không gian tên duy nhất cho các glass ID
  • Mỗi thành phần glass được gán ID qua .glassEffectID()
  • Khi isExpanded thay đổi, các nút xuất hiện/biến mất với hiệu ứng morphing
  • Animation .bouncy tạo hiệu ứng bật nẩy tự nhiên

Kết quả rất ấn tượng: nút "Expand" dường như tách ra thành nhiều nút khi mở rộng, và hợp nhất lại khi thu gọn. Đây chính là materialization trong hành động.

Accessibility: Thiết Kế Cho Mọi Người

Một điểm mình rất thích ở Liquid Glass là khả năng tự động thích ứng với cài đặt accessibility. Trong hầu hết trường hợp, bạn không cần viết thêm code gì — hệ thống lo hết.

Các thích ứng tự động

Liquid Glass tự động điều chỉnh khi người dùng bật:

  • Reduce Transparency: Glass tự động đục hơn để cải thiện khả năng đọc
  • Increase Contrast: Viền và nội dung được tăng cường độ tương phản
  • Reduce Motion: Các hiệu ứng animation như morphing, shimmering được giảm thiểu hoặc loại bỏ

Tùy chỉnh accessibility thủ công

Trong một số trường hợp đặc biệt, bạn có thể muốn kiểm soát chi tiết hơn. SwiftUI cung cấp environment value để đọc cài đặt người dùng:

struct AccessibleGlassView: View {
    @Environment(\.accessibilityReduceTransparency) var reduceTransparency
    @Environment(\.accessibilityReduceMotion) var reduceMotion

    var body: some View {
        Text("Accessible Content")
            .padding()
            .glassEffect(reduceTransparency ? .identity : .regular)
    }
}

Lưu ý là trong thực tế, hệ thống tự xử lý accessibility rất tốt rồi. Chỉ tùy chỉnh thủ công khi bạn có lý do cụ thể.

Một ứng dụng hay hơn là dùng reduceMotion để kiểm soát kiểu animation:

struct SmartAnimationView: View {
    @State private var isExpanded = false
    @Environment(\.accessibilityReduceMotion) var reduceMotion
    @Namespace private var namespace

    var body: some View {
        GlassEffectContainer {
            Button(isExpanded ? "Less" : "More") {
                withAnimation(reduceMotion ? .none : .bouncy) {
                    isExpanded.toggle()
                }
            }
            .glassEffect()
            .glassEffectID("main", in: namespace)
        }
    }
}

Nguyên Tắc Thiết Kế và Best Practices

Dùng Liquid Glass hiệu quả không chỉ là vấn đề kỹ thuật — đó còn là vấn đề thiết kế. Apple đưa ra bốn nguyên tắc quan trọng, và thành thật thì bạn nên tuân thủ nghiêm túc.

Nguyên tắc 1: Chỉ Dùng cho Navigation Layer

Đây là nguyên tắc quan trọng nhất. Liquid Glass chỉ nên áp dụng cho lớp điều hướng:

  • Toolbars
  • Tab bars
  • Sidebars
  • Floating buttons
  • Sheets và Popovers

Tuyệt đối không áp dụng glass cho content layers hay full-screen backgrounds. Lạm dụng glass sẽ khiến người dùng khó phân biệt giữa nội dung và điều hướng.

Nguyên tắc 2: Không Xếp Chồng Glass

Không bao giờ đặt glass lên trên glass. Glass không thể sample glass khác — nếu cần nhiều thành phần glass trong cùng khu vực, hãy dùng GlassEffectContainer.

// SAI - Glass xếp chồng
VStack {
    Text("Inner")
        .padding()
        .glassEffect() // Glass bên trong
}
.padding()
.glassEffect() // Glass bên ngoài - KHÔNG ĐƯỢC!

// ĐÚNG - Sử dụng GlassEffectContainer
GlassEffectContainer {
    HStack {
        Text("Item 1").padding().glassEffect()
        Text("Item 2").padding().glassEffect()
    }
}

Nguyên tắc 3: Yêu Cầu của Biến Thể .clear

Khi dùng .clear, hãy đảm bảo ba điều:

  1. Phần tử glass nằm trên nền giàu media (ảnh, video)
  2. Nội dung phía dưới không bị ảnh hưởng bởi dimming
  3. Nội dung phía trên glass phải đậm, sáng và dễ đọc

Nguyên tắc 4: Tinting Có Mục Đích

Chỉ dùng tint cho các hành động chính có mục đích rõ ràng. Mỗi màu tint nên mang ý nghĩa cụ thể trong hệ thống thiết kế của bạn — đừng dùng chỉ vì nó trông đẹp.

Xây Dựng Ví Dụ Thực Tế: Floating Action Toolbar

Giờ hãy kết hợp mọi thứ đã học để xây dựng một Floating Action Toolbar hoàn chỉnh — thanh công cụ nổi có thể mở rộng/thu gọn với morphing mượt mà.

Bước 1: Định nghĩa Model

struct ToolbarAction: Identifiable {
    let id = UUID()
    let icon: String
    let label: String
    let tintColor: Color?
    let action: () -> Void

    init(icon: String, label: String, tintColor: Color? = nil, action: @escaping () -> Void) {
        self.icon = icon
        self.label = label
        self.tintColor = tintColor
        self.action = action
    }
}

Bước 2: Xây dựng Action Button

struct GlassActionButton: View {
    let toolbarAction: ToolbarAction

    var glassVariant: Glass {
        if let color = toolbarAction.tintColor {
            return .regular.tint(color).interactive()
        }
        return .regular.interactive()
    }

    var body: some View {
        Button {
            toolbarAction.action()
        } label: {
            Image(systemName: toolbarAction.icon)
                .font(.title3)
                .foregroundStyle(.white)
                .frame(width: 48, height: 48)
        }
        .glassEffect(glassVariant, in: .circle)
    }
}

Bước 3: Xây dựng Floating Toolbar

struct FloatingActionToolbar: View {
    @State private var isExpanded = false
    @Namespace private var toolbarNamespace

    let actions: [ToolbarAction]

    var body: some View {
        GlassEffectContainer(spacing: 20) {
            // Nút toggle chính
            Button {
                withAnimation(.bouncy(duration: 0.4)) {
                    isExpanded.toggle()
                }
            } label: {
                Image(systemName: isExpanded ? "xmark" : "plus")
                    .font(.title2)
                    .bold()
                    .foregroundStyle(.white)
                    .frame(width: 56, height: 56)
                    .contentTransition(.symbolEffect(.replace))
            }
            .glassEffect(
                .regular.tint(.blue).interactive(),
                in: .circle
            )
            .glassEffectID("toggleButton", in: toolbarNamespace)

            // Các nút hành động con
            if isExpanded {
                ForEach(Array(actions.enumerated()), id: \.element.id) { index, action in
                    GlassActionButton(toolbarAction: action)
                        .glassEffectID("action_\(index)", in: toolbarNamespace)
                        .transition(.scale.combined(with: .opacity))
                }
            }
        }
    }
}

Bước 4: Tích hợp vào màn hình chính

struct ContentView: View {
    @State private var selectedTab = "home"

    let toolbarActions: [ToolbarAction] = [
        ToolbarAction(icon: "camera.fill", label: "Camera", tintColor: .purple) {
            print("Camera tapped")
        },
        ToolbarAction(icon: "photo.fill", label: "Photos", tintColor: .green) {
            print("Photos tapped")
        },
        ToolbarAction(icon: "square.and.pencil", label: "Compose") {
            print("Compose tapped")
        },
        ToolbarAction(icon: "paperplane.fill", label: "Send", tintColor: .orange) {
            print("Send tapped")
        }
    ]

    var body: some View {
        ZStack {
            // Nội dung chính
            ScrollView {
                LazyVStack(spacing: 16) {
                    ForEach(0..<20, id: \.self) { index in
                        RoundedRectangle(cornerRadius: 12)
                            .fill(.ultraThinMaterial)
                            .frame(height: 120)
                            .overlay {
                                Text("Content Card \(index + 1)")
                                    .font(.headline)
                            }
                            .padding(.horizontal)
                    }
                }
                .padding(.top)
            }

            // Floating toolbar ở góc dưới bên phải
            VStack {
                Spacer()
                HStack {
                    Spacer()
                    FloatingActionToolbar(actions: toolbarActions)
                        .padding(.trailing, 24)
                        .padding(.bottom, 40)
                }
            }
        }
    }
}

Phân tích ví dụ

Ví dụ trên kết hợp khá nhiều khái niệm mà chúng ta đã đi qua:

  • GlassEffectContainer nhóm tất cả nút glass, cho phép morphing và tối ưu rendering
  • glassEffectID kết hợp @Namespace tạo hiệu ứng morphing khi nút xuất hiện/biến mất
  • Tinting có mục đích: Nút chính dùng tint xanh, các nút phụ có tint theo chức năng
  • Interactive modifier trên tất cả nút để phản hồi chạm tốt nhất
  • contentTransition cho icon plus/xmark chuyển đổi mượt mà
  • Floating trên navigation layer: Đúng nguyên tắc thiết kế Apple đề ra

Ví Dụ Bổ Sung: Glass Segment Control

Thêm một ví dụ thực tế nữa — Segmented Control với Liquid Glass và morphing cho indicator. Cái này rất hay khi dùng trong thực tế:

struct GlassSegmentedControl: View {
    @Binding var selection: Int
    let segments: [String]
    @Namespace private var segmentNamespace

    var body: some View {
        GlassEffectContainer(spacing: 4) {
            HStack(spacing: 4) {
                ForEach(Array(segments.enumerated()), id: \.offset) { index, title in
                    Button {
                        withAnimation(.smooth(duration: 0.3)) {
                            selection = index
                        }
                    } label: {
                        Text(title)
                            .font(.subheadline)
                            .fontWeight(selection == index ? .bold : .regular)
                            .foregroundStyle(selection == index ? .white : .secondary)
                            .padding(.horizontal, 16)
                            .padding(.vertical, 10)
                    }
                    .glassEffect(
                        selection == index
                            ? .regular.tint(.blue).interactive()
                            : .regular.interactive(),
                        in: .capsule
                    )
                    .glassEffectID("segment_\(index)", in: segmentNamespace)
                }
            }
        }
    }
}

// Sử dụng
struct SegmentDemo: View {
    @State private var selectedSegment = 0

    var body: some View {
        VStack(spacing: 24) {
            GlassSegmentedControl(
                selection: $selectedSegment,
                segments: ["All", "Photos", "Videos", "Music"]
            )

            // Nội dung thay đổi theo segment
            TabView(selection: $selectedSegment) {
                Text("All Content").tag(0)
                Text("Photos Content").tag(1)
                Text("Videos Content").tag(2)
                Text("Music Content").tag(3)
            }
            .tabViewStyle(.page(indexDisplayMode: .never))
        }
    }
}

Khi chuyển đổi giữa các segment, hiệu ứng tint sẽ morphing mượt mà từ segment cũ sang mới nhờ glassEffectIDGlassEffectContainer. Trải nghiệm người dùng thực sự rất tốt.

Mẹo Tối Ưu Hiệu Suất

Apple đã tối ưu Liquid Glass khá tốt, nhưng vẫn có vài mẹo bạn nên biết để app chạy mượt hơn:

  • Luôn dùng GlassEffectContainer: Khi có nhiều thành phần glass, container giúp batch rendering hiệu quả hơn
  • Tránh glass trên ScrollView lớn: Nếu có hàng trăm item trong danh sách, đừng áp dụng glass cho từng item. Chỉ dùng cho các thành phần điều hướng cố định
  • Dùng .identity khi cần: Trong các tình huống glass không cần thiết (screen nhỏ, chế độ tiết kiệm pin), hãy chuyển sang .identity để giảm tải GPU
  • Hạn chế số lượng glass đồng thời: Giữ số thành phần glass hiển thị cùng lúc ở mức tối thiểu cần thiết

Liquid Glass So Với Material Truyền Thống

Để thấy rõ hơn giá trị của Liquid Glass, hãy so sánh nhanh với các material cũ:

  • Blur Material (.ultraThinMaterial, .regularMaterial...): Gaussian blur đơn giản, hiệu ứng tĩnh, không phản hồi tương tác
  • Liquid Glass: Lensing bẻ cong ánh sáng, hiệu ứng động, phản hồi tương tác, hỗ trợ morphing

Nói đơn giản thì blur material giống nhìn qua tấm kính mờ phẳng, còn Liquid Glass giống nhìn qua một giọt nước — ánh sáng bị bẻ cong và tập trung, tạo chiều sâu và chuyển động tự nhiên.

Tương Thích Ngược (Backward Compatibility)

Liquid Glass chỉ có trên iOS 26. Nếu bạn cần hỗ trợ iOS cũ hơn (và hầu hết chúng ta đều phải làm vậy), đây là pattern được khuyến nghị:

struct AdaptiveGlassView: View {
    var body: some View {
        Text("Adaptive Content")
            .padding()
            .modifier(GlassModifier())
    }
}

struct GlassModifier: ViewModifier {
    func body(content: Content) -> some View {
        if #available(iOS 26, *) {
            content
                .glassEffect(.regular, in: .capsule)
        } else {
            content
                .background(.ultraThinMaterial, in: .capsule)
        }
    }
}

Đoạn code trên dùng #available(iOS 26, *) để áp dụng Liquid Glass trên iOS 26+, và fallback sang .ultraThinMaterial cho phiên bản cũ. Đơn giản mà hiệu quả.

Tổng Hợp Nhanh: Các Modifier Liquid Glass

Dưới đây là bảng tổng hợp tất cả API Liquid Glass mà chúng ta đã đi qua — bookmark lại để tham khảo khi cần:

  • .glassEffect() — Hiệu ứng Liquid Glass cơ bản
  • .glassEffect(.regular) — Biến thể mặc định, độ trong suốt trung bình
  • .glassEffect(.clear) — Biến thể trong suốt cao cho nền media
  • .glassEffect(.identity) — Vô hiệu hóa hiệu ứng
  • .regular.tint(_:) — Thêm lớp màu phủ
  • .regular.interactive() — Kích hoạt phản hồi tương tác
  • .glassEffectID(_:in:) — Gán ID cho morphing
  • GlassEffectContainer — Nhóm các thành phần glass
  • .buttonStyle(.glass) — Button style cho hành động phụ
  • .buttonStyle(.glassProminent) — Button style cho hành động chính

Kết Luận

Liquid Glass là một bước ngoặt trong thiết kế giao diện iOS. Apple đã làm rất tốt việc cung cấp bộ API vừa mạnh mẽ vừa dễ sử dụng — bạn có thể tạo giao diện đẹp, hiện đại chỉ với vài dòng code.

Những điểm mấu chốt cần nhớ:

  1. Bắt đầu đơn giản với .glassEffect() — mặc định đã đủ tốt cho hầu hết trường hợp
  2. Chọn đúng biến thể: .regular cho UI thông thường, .clear cho nền media, .identity để tắt
  3. Dùng GlassEffectContainer khi nhóm nhiều thành phần glass
  4. Kích hoạt morphing bằng .glassEffectID()@Namespace
  5. Tuân thủ nguyên tắc thiết kế: Navigation layer only, không xếp chồng, tinting có mục đích
  6. Accessibility tự động: Hệ thống xử lý tốt, chỉ can thiệp khi thật sự cần

Liquid Glass không chỉ là hiệu ứng thị giác — đó là cách tư duy mới về thiết kế, nơi UI tương tác hài hòa với nội dung và phản hồi tự nhiên với người dùng. Hãy thử áp dụng vào dự án của bạn và trải nghiệm sự khác biệt.