← All shorts
20iOS 26+2 min read

GlassEffectContainer: Morphing Glass Elements

GlassEffectContainer groups glass views so they sample light together, and glassEffectID morphs one glass shape into another.

A single .glassEffect() looks right in isolation, but place several glass views next to each other — a row of toolbar buttons, a segmented control — and each one samples only its own small patch of background. The result is a row of mismatched glass blobs instead of one continuous material, because glass cannot sample other glass.

#The new way

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

    var body: some View {
        GlassEffectContainer(spacing: 20) {
            HStack(spacing: 20) {
                Button {
                    withAnimation(.smooth) { isExpanded.toggle() }
                } label: {
                    Image(systemName: "plus")
                        .frame(width: 44, height: 44)
                }
                .glassEffect(.regular.interactive(), in: .circle)
                .glassEffectID("trigger", in: namespace)

                if isExpanded {
                    ForEach(["pencil", "eraser", "trash"], id: \.self) { icon in
                        Button {
                        } label: {
                            Image(systemName: icon)
                                .frame(width: 44, height: 44)
                        }
                        .glassEffect(.regular.interactive(), in: .circle)
                        .glassEffectID(icon, in: namespace)
                    }
                }
            }
            .padding(.horizontal)
        }
    }
}

Toggling isExpanded animates the new buttons in while the whole row keeps a shared, consistent glass appearance.

#Why it matters

  • GlassEffectContainer gives every glass view inside it a shared sampling region, so a group of controls reads as one material instead of separate blobs
  • .glassEffectID(_:in:) paired with a shared @Namespace lets a glass view morph into a differently shaped or positioned one across a state change — the glass equivalent of matchedGeometryEffect
  • The container's spacing parameter should match the actual layout spacing, or the rendered glass gaps look wrong

#Gotcha

This is very new API and still settling — verify the exact GlassEffectContainer initializer and GlassEffectStyle surface against current Apple docs before shipping anything beyond the basics shown here. One confirmed requirement: morphing only happens when both the old and new view share the same glassEffectID value, the same @Namespace, and are both inside the same GlassEffectContainer instance — put either view outside that container and it pops instead of morphing.

swiftuiios26liquid-glass

Related shorts