如何使用 SwiftUI 在 macOS 上可靠地检索窗口的背景颜色?

Posted

技术标签:

【中文标题】如何使用 SwiftUI 在 macOS 上可靠地检索窗口的背景颜色?【英文标题】:How to reliably retrieve a window's background color on macOS with SwiftUI? 【发布时间】:2021-04-28 16:54:22 【问题描述】:

是否有一种 Swifty 方法可以在 macOS 上的 SwiftUI 中检测窗口的背景颜色,无论当前主题(深色模式还是浅色模式)如何都能可靠地工作?

例如,如果要制作一个与窗口背景“融合”的实心矩形,他们会使用哪种颜色?

这个答案建议使用NSColor.xxxBackgroundColor: SwiftUI: Get the Dynamic Background Color (Dark Mode or Light Mode)

但是,这对我来说不太适用。下面是一些测试代码(Xcode 12.5、Swift 5.4),它生成了三个不同的NSColors 矩形。我正在寻找与背景融为一体的那个。

struct TestView: View 
    var body: some View 
        VStack(spacing: 20) 
            Text("This text is on the default background")
            HStack(spacing: 30) 
                Text("windowBackgroundColor")
                    .frame(width: 200, height: 100)
                    .background(Color(NSColor.windowBackgroundColor))
                Text("underPageBackgroundColor")
                    .frame(width: 200, height: 100)
                    .background(Color(NSColor.underPageBackgroundColor))
                Text("textBackgroundColor")
                    .frame(width: 200, height: 100)
                    .background(Color(NSColor.textBackgroundColor))
            
        
        .padding(20)
    

在深色模式下,NSColor.underPageBackgroundColor 似乎与窗口的背景相匹配。

但在 Light 模式下,没有匹配项:

【问题讨论】:

您是在寻找背景提供的颜色还是检查主题?你的问题不是很清楚,我也不能 100% 确定我的答案是否正确。 @xTwisteDx 确实在寻找窗口背景提供的颜色。让我看看我是否可以澄清这个问题。 我猜我要问的是,你想用这些颜色做什么?从我的角度来看,你似乎已经有了颜色,所以对它们做你想做的事。我觉得你需要解释你的预期结果是什么。您似乎已经可靠地获取了您正在寻找的颜色。 啊,我明白了。您需要设置该颜色,否则它不会像您预期的那样显示。无法从视图中“获取”颜色,但是您可以设置颜色。我现在正在更新我的答案。 【参考方案1】:

获取窗口背景的快捷方式

窗口的背景不是由颜色组成的,是NSVisualEffectView.windowBackgroundmaterial

您可以使用以下代码实现:

struct EffectView: NSViewRepresentable 
    @State var material: NSVisualEffectView.Material = .headerView
    @State var blendingMode: NSVisualEffectView.BlendingMode = .withinWindow

    func makeNSView(context: Context) -> NSVisualEffectView 
        let view = NSVisualEffectView()
        view.material = material
        view.blendingMode = blendingMode
        return view
    
    
    func updateNSView(_ nsView: NSVisualEffectView, context: Context) 
        nsView.material = material
        nsView.blendingMode = blendingMode
    

并将其应用于您的视图:

struct ContentView: View 
    var body: some View 
        EffectView(material: .windowBackground)
            .overlay(Text("Window Real Background"))
            .padding(30)
    

输出是这样的(没什么可看的,因为它模仿了背景):

背景之所以是 NSVisualEffectView 是因为 macOS Big Sur 的 Window Tinting,会根据壁纸主色改变背景:

【讨论】:

美丽的东西!事实上,使用.windowBackground 作为材料,我可以使用 EffectView 作为背景并获得我正在寻找的效果。感谢您的基本解释。【参考方案2】:

您可以使用@Environment 变量来获取正在生成的ColorScheme。在 ios 中,我经常这样使用它,但它也应该转换为 MacOS。无法动态获取视图的颜色,因为它是一个不可访问的设定值。您可以做的最好的事情是将视图设置为已知状态,然后根据需要传递该颜色。在我的示例中,我只使用了Color.black and Color.white,但您可以轻松地将任何颜色分配给变量并将其传递给已知值。

@Environment(\.colorScheme) var colorScheme

struct TestView: View 

    var body: some View 
        VStack(spacing: 20) 
            Text("This text is on the default background")
            HStack(spacing: 30) 
                Text("windowBackgroundColor")
                    .frame(width: 200, height: 100)
                    .background(colorScheme == .dark ? Color.black : Color.white)
                Text("underPageBackgroundColor")
                    .frame(width: 200, height: 100)
                    .background(colorScheme == .dark ? Color.black : Color.white)
                Text("textBackgroundColor")
                    .frame(width: 200, height: 100)
                    .background(colorScheme == .dark ? Color.black : Color.white)
            
        
        .background(colorScheme == .dark ? Color.black : Color.white)
        .padding(20)
    

在您的情况下,您可以将颜色替换为您想要使用的系统颜色。基于colorScheme 设置您想要在该视图上使用的特定颜色。比如……

var windowBGColor = NSColor.windowBackgroundColor
var underPageBGColor = NSColor.underPageBackgroundColor
var textBGColor = NSColor.textBackgroundColor

var sampleColor = colorScheme == .dark ? windowBGColor : textBGColor

【讨论】:

谢谢。这是我用来使事情正常工作的解决方法(即将窗口上的背景颜色设置为与当前配色方案有点合作的已知值)但我希望找到一种更可靠的方法来了解实际使用的颜色在运行时使事情更具未来性。真的感觉NSColor.windowBackroundColor应该是它。【参考方案3】:

windowBackground 是真实窗口的背景颜色,您只需将其与真实窗口背景进行比较,而不是与主机控制器视图下方的主题视图进行比较

这是一个层次结构(你可以在视图调试模式下自己验证)

【讨论】:

这让我发现了非常有用的视图调试器,谢谢。

以上是关于如何使用 SwiftUI 在 macOS 上可靠地检索窗口的背景颜色?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 macOS 上检测 SwiftUI 中的键盘事件?

如何在 macOS 上检测 SwiftUI 中的键盘事件?

如何使用 SwiftUI 在 MacOS 上配置多选分段控制

SwiftUI:如何在 macOS 11 上设置工具栏的标题?

SwiftUI - 如何在 macOS 上隐藏窗口标题

SwiftUI:如何在 macOS 应用程序中实现编辑菜单