Spatial SwiftUI: Custom alignment with spatialOverlay
We can use AlignmentID to customize the alignment of a view within a spatialOverlay.
Overview
As we saw in the example for spatialOverlay, we can use common alignment options like leading, trailing, top, bottom, etc. What if we want a bit more control of how our content will be aligned inside the overlay? We can create custom alignments using AlignmentID.
Let’s make one that will position a view at 1/3 of the width of the container.
private struct ThirdHorizontalAlignment: AlignmentID {
static func defaultValue(in context: ViewDimensions) -> CGFloat {
context.width / 3
}
}Usage:
.init(
horizontal: .init(ThirdHorizontalAlignment.self), // our custom alignment
vertical: .center,
depth: .front
)We can create as many of these as we need. Using the type directly anytime we need an alignment can be a little cumbersome. We can extends alignments to add custom values.
private struct ExplicitHorizontalAlignment: AlignmentID {
static func defaultValue(in context: ViewDimensions) -> CGFloat {
context.width * 0.66
}
}
extension HorizontalAlignment {
static let explicit = HorizontalAlignment(ExplicitHorizontalAlignment.self)
}Usage:
.init(horizontal: .explicit, // our custom alignment
vertical: .center,
depth: .front)

Video Demo
Example Code
struct Example101: View {
@State private var alignmentSign: Alignment3D = .init(horizontal: .center, vertical: .center, depth: .front)
var body: some View {
VStack {
Spacer()
// Use an Earth Model with a simple 2D view as an overlay
ModelViewSimple(name: "Earth", bundle: realityKitContentBundle)
.frame(width: 600, height: 600)
.debugBorder3D(.white)
.spatialOverlay(alignment: alignmentSign) {
VStack {
Text("Earth")
.font(.headline)
Text("The only planet known to serve ice cream")
.font(.caption)
}
.padding()
.background(.black)
.cornerRadius(24)
.breakthroughEffect(.prominent)
}
}
.ornament(attachmentAnchor: .scene(.trailing), ornament: {
VStack(alignment: .leading) {
Button(action: {
withAnimation {
alignmentSign = .init(horizontal: .center, vertical: .center, depth: .front)
}
}, label: {
Text("Center Front")
})
Button(action: {
withAnimation {
alignmentSign = .init(horizontal: .leading, vertical: .top, depth: .front)
}
}, label: {
Text("Top Left Front")
})
Button(
action: {
withAnimation {
alignmentSign = .init(
horizontal: .init(ThirdHorizontalAlignment.self),
vertical: .init(ThirdVerticalAlignment.self),
depth: .front
)
}
},label: {
Text("Fractional Alignment")
})
Button(
action: {
withAnimation {
alignmentSign = .init(horizontal: .explicit,
vertical: .explicit,
depth: .front)
}
},label: {
Text("Explicit Alignment")
})
}
.frame(width: 240)
.padding()
.glassBackgroundEffect()
})
}
}
// Custom Fractional Examples
fileprivate struct ThirdHorizontalAlignment: AlignmentID {
static func defaultValue(in context: ViewDimensions) -> CGFloat {
context.width / 3
}
}
fileprivate struct ThirdVerticalAlignment: AlignmentID {
static func defaultValue(in context: ViewDimensions) -> CGFloat {
context.height / 3
}
}
// Explicite Examples: 0.66 on X and 1.0 on Y
fileprivate struct ExplicitHorizontalAlignment: AlignmentID {
static func defaultValue(in context: ViewDimensions) -> CGFloat {
context.width * 0.66
}
}
fileprivate extension HorizontalAlignment {
static let explicit = HorizontalAlignment(ExplicitHorizontalAlignment.self)
}
fileprivate struct ExplicitVerticalAlignment: AlignmentID {
static func defaultValue(in context: ViewDimensions) -> CGFloat {
context.height * 1.0
}
}
fileprivate extension VerticalAlignment {
static let explicit = VerticalAlignment(ExplicitVerticalAlignment.self)
}Support our work so we can continue to bring you new examples and articles.

Follow Step Into Vision