1

我想要一个背景模糊的 NSWindow,所以我创建了一个包装器,在NSVisualEffectView如何在 SwiftUI macOS 应用程序中模糊背景?. 我也尝试使用https://github.com/lukakerr/NSWindowStyles#:~:text=true-,6.%20Vibrant%20background,A,-vibrant来做到这一点。ContentView()NSWindow

struct VisualEffectView: NSViewRepresentable
{
    let material: NSVisualEffectView.Material
    let blendingMode: NSVisualEffectView.BlendingMode
    
    func makeNSView(context: Context) -> NSVisualEffectView
    {
        let visualEffectView = NSVisualEffectView()
        visualEffectView.material = material
        visualEffectView.blendingMode = blendingMode
        visualEffectView.state = NSVisualEffectView.State.active
        return visualEffectView
    }

    func updateNSView(_ visualEffectView: NSVisualEffectView, context: Context)
    {
        visualEffectView.material = material
        visualEffectView.blendingMode = blendingMode
    }
}

但是,它可以工作并且看起来很棒,但是,当我将窗口移动到另一个屏幕时-并在两个屏幕之间暂停窗口,然后将其移动到下一个窗口-它会切掉NSWindow' 的阴影的一部分。

这是移动屏幕时的样子⤵︎

在此处输入图像描述

有没有办法防止这种影子印章的发生?

接口:SwiftUI
生命周期:Appkit AppDelegate

4

1 回答 1

2

弄清楚了!谢天谢地,没有任何黑客攻击

在此处输入图像描述

规则

为了在没有问题中令人讨厌的伪影的情况下实现这种外观,您必须按照 macOS 想要的方式做一些事情。

1.不要设置你的NSWindow.backgroundColor = .clear
这首先是上面那些讨厌的文物的原因!保持窗口颜色不变将确保窗口在更改屏幕时正常工作。NSVisualEffectView捕获窗口后面的图像并将其用作背景,因此无需使任何东西透明。

2.确保包含.titled在窗口的styleMask中!
否则将呈现没有圆角的窗口。如果您尝试向 SwiftUI 视图添加圆角(就像我所做的那样),您仍然会有一个不透明的背景NSWindow。如果您随后将窗口的背景颜色设置为.clear(就像我再次所做的那样),就会出现阴影切割问题!但是,这并不意味着标题栏会妨碍您,它不会,我们稍后会谈到。

3. 将您添加NSVisualEffectView到您的 SwiftUI 视图中!
我发现这比将视觉效果添加到NSWindow.contentView子视图更容易。

解决方案

1. 所以从设置你的NSWindow和 AppDelegate 开始吧!⤵︎
您所做的只是确保标题栏存在但隐藏。

import Cocoa
import SwiftUI

@main
class AppDelegate: NSObject, NSApplicationDelegate {

    var window: NSWindow!

    func applicationDidFinishLaunching(_ aNotification: Notification) {

        // Create the SwiftUI view that provides the window contents.
        let contentView = ContentView()

        // Create the window and set the content view.
        // Note: You can add any styleMasks you want, just don't remove the ones below.
        window = NSWindow(
            contentRect: NSRect(x: 0, y: 0, width: 300, height: 200),
            styleMask: [.titled, .fullSizeContentView],
            backing: .buffered, defer: false)

        // Hide the titlebar
        window.titlebarAppearsTransparent = true
        window.titleVisibility = .hidden

        // Hide all Titlebar Controls
        window.standardWindowButton(.miniaturizeButton)?.isHidden = true
        window.standardWindowButton(.closeButton)?.isHidden = true
        window.standardWindowButton(.zoomButton)?.isHidden = true

        // Set the contentView to the SwiftUI ContentView()
        window.contentView = NSHostingView(rootView: contentView)

        // Make sure the window is movable when grabbing it anywhere
        window.isMovableByWindowBackground = true

        // Saves frame position between opening / closing
        window.setFrameAutosaveName("Main Window")

        // Display the window
        window.makeKeyAndOrderFront(nil)
        window.center()
    }

    func applicationWillTerminate(_ aNotification: Notification) {
        // Insert code here to tear down your application
    }
}

此时您的窗口可能看起来像这样(如果从空白项目开始)。您可以看到“Hello world!” 由于标题栏,不完全居中。⤵︎

在此处输入图像描述


2.NSWindow设置好后,是时候做ContentView()⤵︎
在这里您只想创建一个包装器NSVisualEffectView并将其添加为背景。然后确保从视图中移除安全区域!这可以确保消除标题栏在视图中占用的任何空间。

import SwiftUI

struct ContentView: View {
    var body: some View {
        Text("Hello, World!")
            .frame(maxWidth: .infinity, maxHeight: .infinity)
            .background(VisualEffectView(material: .popover, blendingMode: .behindWindow))

            // Very important! (You could technically just ignore the top so you do you)
            .edgesIgnoringSafeArea(.all)
    }
}

/// Takes the image directly behind the window and uses that to create a blurred material. It can technically be added anywhere but most often it's used as a backing material for sidebars and full windows.
struct VisualEffectView: NSViewRepresentable {
    let material: NSVisualEffectView.Material
    let blendingMode: NSVisualEffectView.BlendingMode

    func makeNSView(context: Context) -> NSVisualEffectView {
        let visualEffectView = NSVisualEffectView()
        visualEffectView.material = material
        visualEffectView.blendingMode = blendingMode
        visualEffectView.state = NSVisualEffectView.State.active
        return visualEffectView
    }

    func updateNSView(_ visualEffectView: NSVisualEffectView, context: Context) {
        visualEffectView.material = material
        visualEffectView.blendingMode = blendingMode
    }
}

此时,您的视图应该看起来像您想要的那样,没有任何负面影响!享受 <3 (如果您对此解决方案有任何问题,请发表评论!)

在此处输入图像描述

资源

感谢@eonil 以这种巧妙的方式保持圆角。没有这个答案就想不通⤵︎
https://stackoverflow.com/a/27613308/13142325

感谢 lukakerr 制作了这个 NSWindow 样式列表! https://github.com/lukakerr/NSWindowStyles

于 2021-05-29T23:19:37.407 回答