Text views that rest within SwiftUI popover (or NSPopover) components no longer adjust their vibrancy and color values correctly. This results in text being "washed out" if rendered over a background that clashes with the text color. This feels like a significant regression with wide-reaching impact.
Reproducing this is trivial, can be done in pure SwiftUI or a combination of SwiftUI and AppKit, and the differences between Monterey and Ventura can be seen clearly. See here for a sample project: https://github.com/pwerry/BrokenTextVibrancyOnVentura
The workaround is to fall back to using NSTextView and NSTextField components wrapped in an NSViewRepresentable.
Filed a feedback report: FB11742402
I noticed this bug today and came up with the following extension as a workaround for folks that don't want to fall back to using NSTextView or NSTextField as OP did.
extension Color {
/// Creates an adaptive color from the given dynamic `NSColor` instance.
///
/// The color will correctly respond to changes to the current `NSAppearance`.
///
/// Example:
/// ```swift
/// Text("Hello, World!")
/// .foregroundColor(.adaptive(.labelColor))
/// ```
///
/// This is one possible workaround for a SwiftUI bug that prevents colors from being correctly rendered,
/// such as when presented inside a `popover` or `NSPopover`.
///
/// - See Also: [SwiftUI text components no longer adjust their contrast in the presence of vibrancy on Ventura](https://developer.apple.com/forums/thread/721069)
/// - Parameter color: A dynamic `NSColor` instance (e.g. `.labelColor`).
/// - Returns: A color that correctly renders for the current `NSAppearance`.
public static func adaptive(_ color: NSColor) -> Self {
.init(nsColor: NSColor(name: nil) { _ in
return color
})
}
}
This uses NSColor's init(name:dynamicProvider:) method under the hood, which is called when/if the app's appearance changes.
Here is an example of the above helper in use:
ContentView()
.popover(isPresented: $isPresented) {
Text("Fixed Text Vibrancy")
.foregroundColor(.adaptive(.labelColor))
.padding()
}