LoginSignup
8
4

曇りガラス風のビューをNSVisualEffectViewを使ってカスタムする

Last updated at Posted at 2023-12-18

概要

  • 曇りガラスといえば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")
}

成果物

  • アクティブ時
image
  • 非アクティブ時
image

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)
                    }
                }
        }
    }
}
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")
}
8
4
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
4