【问题标题】:My NSWindow's shadow is getting cut off when switching screens?切换屏幕时,我的 NSWindow 的阴影被切断了?
【发布时间】:2021-08-17 16:34:20
【问题描述】:

我想要一个背景模糊的 NSWindow,所以我在How do you blur the background in a SwiftUI macOS application? 的帮助下为NSVisualEffectView 创建了一个包装器,以便在我的ContentView() 中使用。我还尝试使用https://github.com/lukakerr/NSWindowStyles#:~:text=true-,6.%20Vibrant%20background,A,-vibrant 仅使用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

【问题讨论】:

    标签: swift macos swiftui nswindow nsvisualeffectview


    【解决方案1】:

    想通了!谢天谢地,没有任何黑客攻击,哈哈

    规则

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

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

    2。确保在窗口的 styleMask 中包含 .titled
    否则将呈现没有圆角的窗口。如果您尝试在 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
        }
    }
    

    此时,您的视图应该看起来像您想要的那样,没有任何负面影响!享受

    资源

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

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

    【讨论】:

      猜你喜欢
      • 2013-07-26
      • 1970-01-01
      • 1970-01-01
      • 2015-01-13
      • 2015-01-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多