Liquid Glass Common Patterns

Cross-Platform Patterns

State-Based Glass

SwiftUI
.glassEffect(.regular.tint(isActive ? .blue : .clear))
AppKit
glassView.tintColor = isActive ? NSColor.systemBlue.withAlphaComponent(0.3) : nil

Hover Effects

SwiftUI
@State private var isHovered = false
Text("Hover")
    .glassEffect(.regular.tint(isHovered ? .blue : .clear))
    .onHover { isHovered = $0 }
AppKit
override func mouseEntered(with: NSEvent) {
    animator().tintColor = NSColor.systemBlue.withAlphaComponent(0.2)
}

Animated Transitions

SwiftUI
.animation(.spring(duration: 0.3), value: glassState)
AppKit
NSAnimationContext.runAnimationGroup { context in
    context.duration = 0.3
    // animations
}

UI Component Patterns

Glass Card

// SwiftUI
struct GlassCard<Content: View>: View {
    let content: Content
    var body: some View {
        content
            .padding()
            .glassEffect(in: .rect(cornerRadius: 16))
    }
}

// AppKit
class GlassCard: NSView {
    let glass = NSGlassEffectView()
    init(content: NSView) {
        super.init(frame: .zero)
        glass.cornerRadius = 16
        glass.contentView = content
        addSubview(glass)
    }
}

Glass Badge

// SwiftUI
struct GlassBadge: View {
    let count: Int
    var body: some View {
        Text("\(count)")
            .padding(.horizontal, 8)
            .glassEffect(.regular.tint(.red))
    }
}

Glass Toolbar

// SwiftUI
.toolbar {
    ToolbarItemGroup {
        Button("One") { }.buttonStyle(.glass)
        Button("Two") { }.buttonStyle(.glass)
    }
}

Layout Patterns

Grid of Glass Items

// SwiftUI
GlassEffectContainer(spacing: 20) {
    LazyVGrid(columns: [GridItem(.adaptive(minimum: 100))]) {
        ForEach(items) { item in
            ItemView(item).glassEffect()
        }
    }
}

Merging Glass Groups

// SwiftUI with union
@Namespace private var ns
ForEach(items.indices) { i in
    ItemView(items[i])
        .glassEffect()
        .glassEffectUnion(id: groupID(for: i), namespace: ns)
}

Animation Patterns

Pulse Effect

// SwiftUI
@State private var isPulsing = false
Circle()
    .glassEffect(.regular.tint(isPulsing ? .blue : .clear))
    .animation(.easeInOut(duration: 1).repeatForever(), value: isPulsing)
    .onAppear { isPulsing = true }

Morphing Between States

// SwiftUI
@Namespace private var namespace
if expanded {
    LargeView().glassEffect().glassEffectID("morph", in: namespace)
} else {
    SmallView().glassEffect().glassEffectID("morph", in: namespace)
}

Performance Patterns

Lazy Loading Glass

// SwiftUI
ScrollView {
    LazyVStack {
        ForEach(items) { item in
            ItemView(item)
                .glassEffect(isEnabled: item.isVisible)
        }
    }
}

Batch Processing

// AppKit
let container = NSGlassEffectContainerView()
container.spacing = 20
// Add all glass views to container's contentView

Decision Matrix

Use CaseSwiftUIAppKit
Simple glass.glassEffect()NSGlassEffectView
Multiple glassGlassEffectContainerNSGlassEffectContainerView
Button styling.buttonStyle(.glass)Custom GlassButton class
Morphing.glassEffectID()Manual animation
PerformanceContainer + lazyContainer + batch

Tips & Tricks

  1. Merge Control: Adjust container spacing to control merge distance
  2. State Changes: Use tint color for visual feedback
  3. Touch Feedback: Enable .interactive() for user interaction
  4. Performance: Disable glass when off-screen
  5. Consistency: Match corner radius across app

See Also