Догадаться! К счастью, без каких-либо хаков, лол
Правила
Чтобы добиться такого вида без неприятных артефактов в вопросе, вам нужно сделать несколько вещей так, как того хочет 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
}
}
На этом этапе ваше окно, вероятно, будет выглядеть примерно так (если вы начинаете с пустого проекта). Вы можете увидеть «Привет, мир!» не совсем по центру из-за строки заголовка. ⤵︎
<сильный>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
person
tyirvine
schedule
29.05.2021