HomeiOS DevelopmentHow you can create a liquid glass toolbar button that adapt to...

How you can create a liquid glass toolbar button that adapt to background content material


I’ve the next code attempting to create a two rows toolbar as I’ve seen from iOS Safari 26, however the issue is the ToolbarButton struct would not actually adapt to the background colour, it stayed darkish when on darkish background.

I attempted to make use of .glassEffect but it surely creates a small bubble relying on the form of the image, which does not look good with different buttons on the toolbar. I additionally tried to make use of .buttonStyle(.glass) which additionally creates a capsule bubble round every button as effectively.

I additionally tried to group all of the buttons with the glass container in a namespace, that is shut but it surely simply teams all my buttons, after I wished to share the one glass panel with the search area.

All I actually wished was only for the Picture foreground colour to be adaptive to the background scrollview’s content material.

// MARK: - Toolbar Button, however not adaptive to background content material colour
struct ToolbarButton: View {
    var icon: String
    var label: String
    var isSelected: Bool
    var motion: () -> Void

    var physique: some View {
        Picture(systemName: icon)
            .font(.system(measurement: 18, weight: .semibold))
            .symbolVariant(isSelected ? .fill : .none)
            .body(maxWidth: .infinity)
            .padding(.vertical, 6)
            .contentShape(Rectangle())
            .onTapGesture {
                motion()
            }
    }
}
import SwiftUI

@fundamental
struct GlassToolbarDemoApp: App {
    var physique: some Scene {
        WindowGroup {
            DemoView()
        }
    }
}

struct DemoView: View {
    @State non-public var selectedTab: Tab = .dwelling
    @State non-public var question: String = ""
    @Atmosphere(.horizontalSizeClass) non-public var hSizeClass

    var physique: some View {
        ZStack {
            SampleBackgroundScroll()
                .ignoresSafeArea() // let content material run below the glass for a greater impact
        }
        // Pin our two-row glass toolbar to the TOP. Change to .backside to make it a backside toolbar.
        .safeAreaInset(edge: .backside) {
            GlassBar {
                // ACCESSORY ROW (above the buttons)
                HStack(spacing: 10) {
                    // Search area
                    HStack(spacing: 8) {
                        Picture(systemName: "magnifyingglass")
                        TextField("Search…", textual content: $question)
                            .textInputAutocapitalization(.by no means)
                    }
                    .padding(.horizontal, 12)
                    .padding(.vertical, 10)
                    .background(
                        RoundedRectangle(cornerRadius: 14, fashion: .steady)
                            .fill(.thinMaterial)
                    )
                }
                .body(maxWidth: .infinity, alignment: .main)
                .padding(.horizontal, 2)
            } mainRow: {
                // MAIN TOOLBAR BUTTONS ROW
                if hSizeClass == .compact {
                    HStack(spacing: 0) {
                        ToolbarButton(icon: "home.fill", label: "Residence", isSelected: selectedTab == .dwelling) { selectedTab = .dwelling }
                        ToolbarButton(icon: "textual content.magnifyingglass", label: "Uncover", isSelected: selectedTab == .uncover) { selectedTab = .uncover }
                        ToolbarButton(icon: "plus.circle.fill", label: "Add", isSelected: selectedTab == .add) { selectedTab = .add }
                        ToolbarButton(icon: "star.fill", label: "Saved", isSelected: selectedTab == .saved) { selectedTab = .saved }
                        ToolbarButton(icon: "individual.fill", label: "Me", isSelected: selectedTab == .me) { selectedTab = .me }
                    }
                } else {
                    // On iPad / common width, present accent and buttons in ONE row (no peak waste)
                    HStack(spacing: 16) {
                        ToolbarButton(icon: "home.fill", label: "Residence", isSelected: selectedTab == .dwelling) { selectedTab = .dwelling }
                        ToolbarButton(icon: "textual content.magnifyingglass", label: "Uncover", isSelected: selectedTab == .uncover) { selectedTab = .uncover }
                        ToolbarButton(icon: "plus.circle.fill", label: "Add", isSelected: selectedTab == .add) { selectedTab = .add }
                        ToolbarButton(icon: "star.fill", label: "Saved", isSelected: selectedTab == .saved) { selectedTab = .saved }
                        ToolbarButton(icon: "individual.fill", label: "Me", isSelected: selectedTab == .me) { selectedTab = .me }

                        Spacer(minLength: 16)

                        HStack(spacing: 10) {
                            HStack(spacing: 8) {
                                Picture(systemName: "magnifyingglass")
                                TextField("Search…", textual content: $question)
                                    .textInputAutocapitalization(.by no means)
                            }
                            .padding(.horizontal, 12)
                            .padding(.vertical, 10)
                            .background(
                                RoundedRectangle(cornerRadius: 14, fashion: .steady)
                                    .fill(.thinMaterial)
                            )
                        }
                        .body(maxWidth: 420)
                    }
                }
            }
        }
    }
}

// MARK: - Glass Bar (two-row toolbar)
struct GlassBar<Accent: View, Primary: View>: View {
    var accessoryRow: Accent
    var mainRow: Primary

    init(@ViewBuilder accent: () -> Accent, @ViewBuilder mainRow: () -> Primary) {
        self.accessoryRow = accent()
        self.mainRow = mainRow()
    }

    var physique: some View {
        VStack(spacing: 8) {
            accessoryRow
            Divider().opacity(0.25)
            mainRow
        }
        .padding(.horizontal, 14)
        .padding(.vertical, 10)
        .body(maxWidth: .infinity)
        .background(GlassBackground(cornerRadius: 26))
        .padding(.horizontal)
        .padding(.high, 6)
        .shadow(colour: .black.opacity(0.12), radius: 20, y: 10)
    }
}

// MARK: - Glass Background helper
struct GlassBackground: View {
    var cornerRadius: CGFloat = 10

    var physique: some View {
        Group {
            if #accessible(iOS 26.0, *) {
                RoundedRectangle(cornerRadius: cornerRadius, fashion: .steady)
                    .fill(.clear)
                    .glassEffect(.clear)
            } else {
                // Fallback for older OSes: use materials to approximate the impact.
                RoundedRectangle(cornerRadius: cornerRadius, fashion: .steady)
                    .fill(.ultraThinMaterial)
                    .overlay(
                        // delicate spotlight to simulate glass rim
                        RoundedRectangle(cornerRadius: cornerRadius, fashion: .steady)
                            .strokeBorder(.white.opacity(0.08), lineWidth: 1)
                    )
            }
        }
    }
}

// MARK: - Toolbar Button
struct ToolbarButton: View {
    var icon: String
    var label: String
    var isSelected: Bool
    var motion: () -> Void

    var physique: some View {
        Picture(systemName: icon)
            .font(.system(measurement: 18, weight: .semibold))
            .symbolVariant(isSelected ? .fill : .none)

            .body(maxWidth: .infinity)
            .padding(.vertical, 6)
            .contentShape(Rectangle())
            .onTapGesture {
                motion()
            }
    }
}

// MARK: - Demo background content material to indicate gentle/darkish behind glass
struct SampleBackgroundScroll: View {
    var physique: some View {
        ScrollView {
            LazyVStack(spacing: 0) {
                demoBlock(.gentle, title: "Sunny Paper")
                demoBlock(.darkish, title: "Night time Slate")
                demoBlock(.colourful, title: "Aurora Cyan")
                demoBlock(.gentle, title: "Porcelain")
                demoBlock(.darkish, title: "Graphite")
                demoBlock(.colorfulAlt, title: "Sundown Mix")
                demoBlock(.gentle, title: "Foggy White")
                demoBlock(.darkish, title: "Charcoal")
            }
        }
    }

    @ViewBuilder
    func demoBlock(_ fashion: BlockStyle, title: String) -> some View {
        ZStack {
            swap fashion {
            case .gentle:
                LinearGradient(colours: [Color.white, Color(white: 0.93)], startPoint: .topLeading, endPoint: .bottomTrailing)
            case .darkish:
                LinearGradient(colours: [Color.black, Color(white: 0.15)], startPoint: .high, endPoint: .backside)
            case .colourful:
                LinearGradient(colours: [Color.cyan.opacity(0.7), Color.blue.opacity(0.4)], startPoint: .topLeading, endPoint: .bottomTrailing)
            case .colorfulAlt:
                LinearGradient(colours: [Color.purple, Color.orange], startPoint: .topLeading, endPoint: .bottomTrailing)
            }

            VStack(spacing: 12) {
                Textual content(title)
                    .font(.system(measurement: 28, weight: .daring))
                    .foregroundStyle(fashion == .darkish ? .white : .major)
                Textual content("Scroll to see how the glass adapts to totally different backgrounds.")
                    .font(.system(measurement: 15))
                    .foregroundStyle(fashion == .darkish ? .white.opacity(0.85) : .secondary)
            }
            .padding(.high, 120)
        }
        .body(peak: 360)
    }

    enum BlockStyle { case gentle, darkish, colourful, colorfulAlt }
}

// MARK: - Tabs
enum Tab { case dwelling, uncover, add, saved, me }

#Preview {
    DemoView()
}


How you can create a liquid glass toolbar button that adapt to background content material

RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

- Advertisment -
Google search engine

Most Popular

Recent Comments