惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

V
V2EX
宝玉的分享
宝玉的分享
Jina AI
Jina AI
IT之家
IT之家
博客园 - Franky
MyScale Blog
MyScale Blog
Y
Y Combinator Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
I
InfoQ
雷峰网
雷峰网
WordPress大学
WordPress大学
Microsoft Security Blog
Microsoft Security Blog
Google DeepMind News
Google DeepMind News
美团技术团队
S
SegmentFault 最新的问题
罗磊的独立博客
博客园 - 聂微东
大猫的无限游戏
大猫的无限游戏
H
Help Net Security
D
Docker
博客园 - 司徒正美
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
阮一峰的网络日志
阮一峰的网络日志
M
MIT News - Artificial intelligence

Inside Nutrient

A guide to the invisible work behind documents Introducing Nutrient Documents for Salesforce: Native document generation and signing Document AI vs. traditional OCR: Choosing between OCR, AI, and hybrid pipelines PDF SDK compliance and security evaluation checklist for enterprise teams (2026) Invariant Corp replaces paper processes with Nutrient Workflow and scales without limits What is process mapping? A complete guide Nutrient vs. Conga Composer for Salesforce document generation (2026) Document routing: How to automate document distribution The CTO’s AI playbook: Why accountability architecture beats orchestration Compliance workflow automation: Why built-in compliance is table stakes Workflow diagrams: Examples, symbols, and how to build one that actually runs Digital forms: Replace paper forms with automated workflows Approval workflow software: How to automate approvals Why document-centric automation is different The CEO’s AI playbook: Why decision architecture beats model selection Nutrient SDK product updates for Q1 2026 PDF redaction verification: How to prove sensitive data is permanently removed What is a VPAT? The complete guide to accessibility conformance reports What is PDF/UA? The accessible PDF standard explained Salesforce eSignatures: Generate, sign, and track documents in one flow Online document viewer: Options, tradeoffs, and how to embed one Document viewer for web apps: React, Vue, Angular (2026) Best document viewers in 2026: A buyer’s guide How to edit a PDF in Python: Add text, images, and annotations Nutrient advances Workflow platform with agentic AI for enterprise-grade speed and consistency in document-heavy operations How to create a Salesforce quote template from opportunity data The business case for accessibility: Five ways it drives enterprise value Python PDF library comparison (2026): 7 libraries for developers Why your AI agent hallucinates PDF table data PDF.js limitations: When to upgrade to a commercial PDF SDK
Showing Popovers from Ornaments on visionOS
Vishnu Dutt · 2024-06-25 · via Inside Nutrient

Our PDF SDK supports Apple’s visionOS alongside iOS and Mac Catalyst. While adding this support, we encountered some challenges related to displaying UIKit popovers from ornaments built in SwiftUI. We have a UIKit view controller, PDFViewController, which displays PDF documents and is shown with different tools in the navigation bar. We also have existing code for iOS that shows modal views for some of these tools as popovers. To display the tools in ornaments on visionOS, we wanted to reuse this existing code.

iOS toolbar showing the share popover

In this blog post, we’ll delve into the challenges we faced when implementing this. But first, we’ll provide a quick overview of ornaments on visionOS.

Introduction to Ornaments and Different Alignments

On visionOS, an ornament(opens in a new tab) floats alongside an app’s window, holding extra controls and information without obstructing the main content.

Ornaments can appear on any side of a window. Inside an ornament, you can place buttons, sliders, or any other controls your app requires. The system already uses ornaments for elements like toolbars and tab bars. However, you can also build your own custom ornaments for unique features in your app.

Default visionOS Ornaments

By default, visionOS displays toolbars and tab bars as ornaments. Here’s an example of how they’re displayed:

struct ViewWithDefaultToolbarAndTabView: View {

var body: some View {

TabView {

Text("Toolbar")

.tabItem {

Button("Share", systemImage: "square.and.arrow.up") { }

}

.toolbar {

ToolbarItem(placement: .bottomOrnament) {

Button("Text", systemImage: "doc.text") { }

}

ToolbarItem(placement: .bottomOrnament) {

Button("Draw", systemImage: "pencil.line") { }

}

ToolbarItem(placement: .bottomOrnament) {

Button("Eraser", systemImage: "eraser") { }

}

}

Text("Search")

.tabItem {

Button("Search", systemImage: "magnifyingglass") { }

}

}

}

}

visionOS showing toolbars and tab bars as ornaments

Displaying Custom Ornaments on a SwiftUI View

You can also display custom ornaments with an ornament view modifier on any SwiftUI view. Here’s an example:

struct ViewWithCustomOrnament: View {

@State private var showingPopover = false

@State var searchList: [String] = ["Item 1", "Item 2", "Item 3", "Item 4", "Item 5"]

var body: some View {

Text("PDFView")

.ornament(

visibility: .visible,

attachmentAnchor: .scene(.trailing),

contentAlignment: .top

) {

ToolbarOrnament()

}

.ornament(

visibility: .visible,

attachmentAnchor: .scene(.top),

contentAlignment: .trailing

) {

HStack {

Button("Share", systemImage: "square.and.arrow.up") {

// Show sharing UI.

}

SearchBarOrnamentItem()

}

.labelStyle(.iconOnly)

.padding(20)

.glassBackgroundEffect()

}

}

}

visionOS showing custom ornaments

The example above uses the ornament view modifier, which accepts several parameters. The visibility parameter gives you the ability to dictate when an ornament is displayed.

The attachmentAnchor and contentAlignment parameters let you manage an ornament’s location. They provides the flexibility to specify the exact point in the scene where the ornament should be attached.

The final parameter for the ornament view modifier is the ViewBuilder closure. This allows you to specify an ornament’s content.

Additionally, the glassBackgroundEffect view modifier can be used to apply a visionOS-style background to content.

Displaying Custom Ornaments on a UIKit View Controller

Ornaments can also be shown on any UIKit view controllers using the view controller’s ornaments property, which is an array of UIHostingOrnaments. A UIHostingOrnament is created with a SwiftUI view as a parameter. Here’s an example:

class PDFViewController: UIViewController {

func setUpOrnaments() {

self.ornaments = [

UIHostingOrnament(sceneAnchor: .trailing) { ToolbarOrnament() },

UIHostingOrnament(sceneAnchor: .top) { NavigationBarOrnament() }

]

}

}

This is how we added different toolbars as ornaments to our PDFViewController.

Displaying custom ornaments on UIKit view controllers

Displaying a SwiftUI Popover on a Button Tap in the Ornament UI

You can show a popover using SwiftUI when a button in the ornament user interface (UI) is tapped. Here’s how:

struct SearchBarOrnamentItem: View {

@State private var showingPopover = false

@State var searchList: [String] = ["Item 1", "Item 2", "Item 3", "Item 4", "Item 5"]

var body: some View {

Button("Search", systemImage: "magnifyingglass") {

showingPopover = true

}

Spacer(minLength: 10)

.popover(isPresented: self.$showingPopover,

attachmentAnchor: .point(.bottom),

arrowEdge: .bottom,

content: {

ForEach(searchList, id: \.self) { item in

Text(item)

.padding()

}

.padding()

.presentationCompactAdaptation(.none)

})

}

}

Displaying a SwiftUI popover on a visionOS ornament

Displaying a UIKit Popover on a Button Tap in the Ornament UI (The Actual Problem)

Now that we’ve covered the ornament UI, this section will cover the specific problem we encountered.

We wanted to reuse the popover UI shown when tapping different tool buttons in the ornament UI. The popover view controller needs a UIKit view as the source view to figure out the frame or position of the popover view. But we only have SwiftUI views in the ornament toolbar.

To solve this problem, we created a SwiftUI button wrapper, AnchorButton, which contains an internal UIKit view. AnchorButton uses a ZStack to display a SwiftUI view on top of a UIKit view. Now, since the SwiftUI view and UIKit-wrapped UI view are at the same position in the view hierarchy, we can use the UIKit view as the source view to display the existing UIKit popover. Refer to AnchorButton.swift(opens in a new tab) for more details and the source code of the wrapper in the example project(opens in a new tab).

With the fix above, we were able to reuse our prebuilt UIKit popover UIs, but we got the following error in the console:

Trying to convert coordinates between views that are in different UIWindows, which isn’t supported, Use convertPoint: fromCoordinateSpace: instead.

This occurred because ornaments are in different windows and coordinates need to be converted to destination coordinates. The code below shows how:

class PDFViewController: UIViewController {

func showPopover(sourceView: UIView) {

let popoverViewController = PopoverViewController()

popoverViewController.modalPresentationStyle = .popover

if let popoverController = popoverViewController.popoverPresentationController {

let pointInView = view.convert(sourceView.bounds, from: sourceView.coordinateSpace)

popoverController.sourceView = view

popoverController.sourceRect = pointInView

popoverController.permittedArrowDirections = .up

}

self.present(popoverViewController, animated: true)

}

}

visionOS ornament showing the share popover

Conclusion

visionOS ornaments float alongside an app’s window, holding extra controls and information without obstructing the main content. In this post, we’ve seen how to show ornaments when the content is implemented using either SwiftUI views or UIKit view controllers. We also saw how to present popovers from ornaments, using either SwiftUI or UIKit to display the popover content.

The full source code for the examples used in this post is available on GitHub(opens in a new tab).

See our getting started guides for information on how to integrate PSPDFKit into your visionOS app. Feel free to reach out to us if you run into any problems or have questions. We’re happy to help!