Spatial SwiftUI: Window Ornaments

Ornaments allow us to decorate our windows with information and controls that may not fit in the main window region.

Overview

We can add a simple ornament to our windows like so.

{...} // root view
.ornament(attachmentAnchor: .scene(.topLeading)) {
    Text("Ornament")
        .padding(20)
        .cornerRadius(20)
}

This will create a text view that is anchored to the top leading area of the window. Anchors in this context refer to UnitPoint values. We can use one of the pre-built properties (.top, .center .leading, etc.). We can also specify an X and Y value if we need a custom position.

We can control ornament visibility if we need to show and hide these. This will only show when ourState is true.

{...} // root view
.ornament(visibility: ourState, attachmentAnchor: .scene(.topLeading)) {
    Text("Ornament")
        .padding(20)
        .cornerRadius(20)
}

We can set the alignment of the view on the anchor as well.

{...} // root view
.ornament(visibility: ourState, attachmentAnchor: .scene(ourAnchor), contentAlignment: ourAlignment) {
    Text("Ornament")
        .padding(20)
        .cornerRadius(20)
}

Later–in the labs–we will combine ornament with rotation and transforms to create more advanced layouts.

Video Demo

Full Example Code

Please excuse my repetitive code…

struct Example037: View {
    @State var ornamentVisibility: Visibility = .hidden
    @State var ornamentAnchor: UnitPoint = .top
    @State var ornamentAlignment: Alignment = .center

    // convenience functions so I don't have to repeat "withAnimation" in every button
    func setAnchor(_ newAnchor: UnitPoint) {
        withAnimation {
            ornamentAnchor = newAnchor
        }
    }

    func setAlignment(_ newAlignment: Alignment) {
        withAnimation {
            ornamentAlignment = newAlignment
        }
    }

    var body: some View {
        ZStack {
            VStack(spacing: 20) {
                Button{
                    withAnimation {
                        ornamentVisibility = ornamentVisibility == .visible ? .hidden : .visible
                    }
                } label: {
                    Text("Toggle Ornament")
                }

                Spacer()

                HStack(spacing: 40) {

                    // Anchor buttons
                    VStack {
                        Text("Anchor")
                        HStack(alignment: .center) {
                            Button{
                                setAnchor(.topLeading)
                            } label: {
                                Image(systemName: "arrow.up.left")
                            }

                            Button{
                                setAnchor(.top)
                            } label: {
                                Image(systemName: "arrow.up")
                            }

                            Button{
                                setAnchor(.topTrailing)
                            } label: {
                                Image(systemName: "arrow.up.right")
                            }
                        }

                        HStack(alignment: .center) {
                            Button{
                                setAnchor(.leading)
                            } label: {
                                Image(systemName: "arrow.left")
                            }

                            Button{
                                setAnchor(.center)
                            } label: {
                                Image(systemName: "square")
                            }

                            Button{
                                setAnchor(.trailing)
                            } label: {
                                Image(systemName: "arrow.right")
                            }
                        }

                        HStack(alignment: .center) {
                            Button{
                                setAnchor(.bottomLeading)
                            } label: {
                                Image(systemName: "arrow.down.left")
                            }

                            Button{
                                setAnchor(.bottom)
                            } label: {
                                Image(systemName: "arrow.down")
                            }

                            Button{
                                setAnchor(.bottomTrailing)
                            } label: {
                                Image(systemName: "arrow.down.right")
                            }

                        }
                    }
                    
                    // Alignment buttons
                    VStack {
                        Text("Alignment")
                        HStack(alignment: .center) {
                            Button{
                                setAlignment(.topLeading)
                            } label: {
                                Image(systemName: "arrow.up.left")
                            }

                            Button{
                                setAlignment(.top)
                            } label: {
                                Image(systemName: "arrow.up")
                            }

                            Button{
                                setAlignment(.topTrailing)
                            } label: {
                                Image(systemName: "arrow.up.right")
                            }
                        }

                        HStack(alignment: .center) {
                            Button{
                                setAlignment(.leading)
                            } label: {
                                Image(systemName: "arrow.left")
                            }


                            Button{
                                setAlignment(.center)
                            } label: {
                                Image(systemName: "square")
                            }


                            Button{
                                setAlignment(.trailing)
                            } label: {
                                Image(systemName: "arrow.right")
                            }
                        }

                        HStack(alignment: .center) {
                            Button{
                                setAlignment(.bottomLeading)
                            } label: {
                                Image(systemName: "arrow.down.left")
                            }

                            Button{
                                setAlignment(.bottom)
                            } label: {
                                Image(systemName: "arrow.down")
                            }

                            Button{
                                setAlignment(.bottomTrailing)
                            } label: {
                                Image(systemName: "arrow.down.right")
                            }
                        }
                    }
                }
            }
            .padding(100)
            .ornament(
                visibility: ornamentVisibility,
                attachmentAnchor: .scene(ornamentAnchor),
                contentAlignment: ornamentAlignment
            ) {
                Text("Ornament")
                    .padding(20)
                    .background(.stepBlue)
                    .cornerRadius(20)
            }
        }
    }
}

Support our work so we can continue to bring you new examples and articles.

Questions or feedback?