Categories
iOS Development

SwiftUI .ornaments modifier for visionOS App on Vision Pro

Currently, I am experimenting on a Vision Pro app as a side project. One thing I was not aware of for quite some time was this new paradigm with the fancy name Ornaments only used on visionOS. I just happily added buttons near the bottom edge of my view. However, using an ornament is the way to go. It is basically a kind of elevated group of controls at the bottom of the view. You can read more on it in Apple’s Human Interface Guidelines (HIG): https://developer.apple.com/design/human-interface-guidelines/ornaments/

Using new SwiftUI features

If you want to learn about other new SwiftUI features you can use for making your visionOS app feeling “more home”, please checkout my other article:
https://mic.st/blog/develop-and-release-a-visionos-app-without-vision-pro/

How to implement ornaments

When I first tried implementing my ornament, it just did not work. And I could not really find any useful information on why it did not work. What I found out about after sleeping over it: Your scene must not be of window style must not be .volumetric it has to be .automatic (or plain). If it’s your initial screen also make sure to set UIWindowSceneSessionRoleApplication for the key UIApplicationPreferredDefaultSceneSessionRole inside of your info.plist.

This means your info.plist should have this content:

<dict>
 <key>UIApplicationSceneManifest</key>
 <dict>
  <key>UISceneConfigurations</key>
  <dict/>
  <key>UIApplicationPreferredDefaultSceneSessionRole</key>
  <string>UIWindowSceneSessionRoleApplication</string>
 </dict>
</dict>
Code language: HTML, XML (xml)

And your initial WindowGroup should look similar to this (so you can still set the view’s bounds if you want):

WindowGroup(id: someIDIfNeeded) {
 MyFunkyView()
}
.windowStyle(.automatic) // or .plain
.defaultSize(Size3D(width: 1.8, height: 1, depth: 0.1), in: .meters)Code language: Swift (swift)

The following won’t work. Or at least the ornament will not be shown:

WindowGroup(id: someIDIfNeeded) {
 MyFunkyView()
}
.windowStyle(.volumetric)
.defaultSize(Size3D(width: 1.8, height: 1, depth: 0.1), in: .meters)Code language: Swift (swift)

As soon as you have this set up, you can just use the .ornaments modifier e.g. on your NavigationStack like this:

.ornament(visibility: .visible, attachmentAnchor: .scene(.bottom), contentAlignment: .center) {
 HStack(spacing: 16) {
  Text("HELLO ORNAMENT 🚀")
  Button {
   print("Tapped that!")
  } label: {
   Label("Big Button", systemImage: "hare")
    .padding(16)
  }
 }
 .font(.extraLargeTitle)
 .padding(32)
 .glassBackgroundEffect()
}Code language: Swift (swift)

Eventually, this is how the ornament above will look like:

Screenshot of visionOS app showing an ornament rendered from code previously shown in this post.
Rendering of the code above inside of the Vision Pro simulator

Conclusion

If your ornament is not show, make sure you are showing it on an WindowGroup using .plain window style. Otherwise, it is just not shown for some reason.

Happy Coding! 🚀

Leave a Reply

Your email address will not be published. Required fields are marked *