概要
- 曇りガラスといえばSwiftUIでMaterialが追加されましたが、ウインドウが非アクティブのときにエフェクトが切れてしまうのが気に入りませんでした。
- そこで
NSVisualEffectView
を使い、ウインドウのアクティブ状態に関わらずエフェクトを維持するようにカスタムしてみます。
追記
- @Uhucream さんに、NSVisualEffectViewを使わずに実現する方法を教えていただきました!
https://qiita.com/IKEH/items/2a1720a328188be052a2#comment-08fd833a98081568f776
実は、NSVisualEffectView を使わずとも、 .environment(.controlActiveState, .active) を .background(.ultraThinMaterial) の後に指定してやるだけで、やりたいことが実現できます!.environment で、要素のアクティブ状態を固定してやる、という発想です👀
余談ですが、同じノリの話として、SwiftUI の Material は UIKit の UIBlurEffect.Style と比較して、**MaterialLight / **MaterialDark がないかと思いますが、その代わりに、.environment(.colorScheme, .light) / .environment(.colorScheme, .dark) を該当箇所に指定してやることで、**MaterialLight / **MaterialDark と同じ見た目を表現できるようになっています
ZStack {
Color.clear
.background(.ultraThinMaterial)
.environment(\.controlActiveState, .active)
// .environment(\.colorScheme, .light)
.environment(\.colorScheme, .dark)
Text("ultraThinMaterial")
}
成果物
- アクティブ時
- 非アクティブ時
Gist
実装
コード
- まず下準備として、ウインドウの背景を透明にしておきます
import SwiftUI
@main
struct MaterialDemoMatomeApp: App {
var body: some Scene {
WindowGroup {
ContentView()
.onReceive(NotificationCenter.default.publisher(for: NSWindow.didBecomeKeyNotification)) { notification in
if let window = notification.object as? NSWindow {
window.backgroundColor = NSColor(red: 0, green: 0, blue: 0, alpha: 0.001)
}
}
}
}
}
- SwiftUIのViewとして使えるように
NSViewRepresentable
を使用します。 - またウインドウのアクティブ状態に関わらずエフェクトを維持するには、NSVisualEffectView.Stateを、デフォルトの
followsWindowActiveState
からactive
にします。
struct BlurView: NSViewRepresentable {
private let material: NSVisualEffectView.Material
init(material: NSVisualEffectView.Material) {
self.material = material
}
func makeNSView(context: Context) -> some NSVisualEffectView {
let view = NSVisualEffectView()
view.material = material
view.blendingMode = .behindWindow
view.state = .active
return view
}
func updateNSView(_ nsView: NSViewType, context: Context) {
nsView.material = material
}
}
- あとはこれをSwiftUIで利用するだけですね。
ZStack {
Color.clear
.background(BlurView(material: material))
Text("NSVisualEffectView")
}
- 余談ながら、ultraThinMaterialと全く同じ効果になるものは、NSVisualEffectView.Materialには無いようでした。