vibetunnel/apple/docs/liquid-glass/patterns.md
Peter Steinberger 3407cf4f11 add docs
2025-08-06 18:42:51 +02:00

3.9 KiB

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 Case SwiftUI AppKit
Simple glass .glassEffect() NSGlassEffectView
Multiple glass GlassEffectContainer NSGlassEffectContainerView
Button styling .buttonStyle(.glass) Custom GlassButton class
Morphing .glassEffectID() Manual animation
Performance Container + lazy Container + 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