← 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. Place several glass views next to each other, say a row of toolbar buttons or 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. It is 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