如何使用SwiftUI在macOS上可靠地获取窗口的背景色?

9

在SwiftUI的macOS环境下,有没有一种简单的方法来可靠地检测窗口的背景颜色,无论当前主题(暗模式或亮模式)如何?

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

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

然而,对我来说这并不完全适用。下面是一些测试代码(Xcode 12.5,Swift 5.4),它生成了几个不同NSColor的矩形。我正在寻找与背景融合的那一个。

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 似乎与窗口的背景匹配。 enter image description here

但是在浅色模式下,没有任何匹配项: enter image description here


你是在寻找背景提供的颜色还是要检查主题?你的问题不是很清楚,我也不能百分之百确定我的答案是否正确。 - xTwisteDx
@xTwisteDx 确实是在寻找窗口背景提供的颜色。让我看看是否可以澄清这个问题。 - vmallet
我想问的是,你想用这些颜色做什么?从我的角度来看,你已经有了这些颜色,所以可以按照自己的意愿使用它们。我感觉你需要解释一下你期望的结果是什么。看起来你已经可靠地获取了你要找的颜色。 - xTwisteDx
啊,现在我明白了。你需要设置那个颜色,否则它不会像你期望的那样出现。没有办法从视图中“获取”颜色,但是你可以设置颜色。我正在更新我的答案。 - xTwisteDx
3个回答

11

获取窗口背景的Swifty方式

窗口的背景不是由颜色组成的,而是一个带有.windowBackground作为materialNSVisualEffectView

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

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)
    }
}

输出结果如下(看不见,因为它与背景混合):

enter image description here

背景使用NSVisualEffectView的原因是因为macOS Big Sur的窗口色调会根据壁纸主色调改变背景:

enter image description here


1
美妙的事情!确实,使用.windowBackground作为材料,我可以将EffectView用作背景,并获得我正在寻找的效果。感谢您提供的解释。 - vmallet
超酷的,猜想没有办法将这个转换成 NSColor 以便能够放入 SCNScene 中,因为 SCNScene 期望的是一个像 scene.background.contents = CPColor.primaryBackground 这样的 NSColor。 - undefined

0

windowBackground 是真实窗口的背景颜色,你不是将其与真实窗口背景进行比较,而是与托管控制器视图下的主题视图进行比较。

以下是层次结构(你可以在视图调试模式下自行验证):

demo


这让我发现了视图调试器,非常有帮助,谢谢。 - vmallet

0

您可以使用@Environment变量来获取正在生成的ColorScheme。在iOS中,我经常这样使用它,但它也应该适用于MacOS。没有办法动态地获取视图的颜色,因为它是一个不可访问的设置值。您能做的最好的事情就是将视图设置为已知状态,然后根据需要传递该颜色。在我的示例中,我只使用了Color.black和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应该是它。 - vmallet

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接