You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SwiftUI跨平台开发中如何在macOS端控制模糊效果强度

macOS端实现与iOS端fractionComplete等效的可控模糊效果

问题背景

iOS端可通过UIViewPropertyAnimator的fractionComplete属性自由控制UIVisualEffectView的模糊强度,在同时支持iOS、macOS的跨平台SwiftUI项目中,iOS端代码运行效果符合预期,但macOS端NSVisualEffectView未提供公开的、与fractionComplete功能完全一致的属性,无法直接实现同等的模糊强度可控效果,现有实现代码如下:

import SwiftUI

struct ContentView: View {
    var body: some View {
        
        ZStack {
            
            Text("Hello, world!")
                .font(Font.system(size: 50.0))
                .padding()
                .background(Color.yellow)
                .fixedSize()
            
            Text("Hello, world!")
                .font(Font.system(size: 25.0))
                .foregroundColor(Color.blue)
                .padding(20)
                .background(UnicornView())
                .border(Color.blue)
        }

    }
}
#if os(iOS)
struct UnicornView: UIViewControllerRepresentable {
    
    class UnicornControllerModel: UIViewController {
        
        var animator = UIViewPropertyAnimator(duration: 1, curve: .linear)
        var visualEffectView = UIVisualEffectView(effect: nil)
        
        override func viewDidLoad() {
            
            super.viewDidLoad()

            animator.addAnimations {
                self.visualEffectView.effect = UIBlurEffect(style: .systemUltraThinMaterial)
            }
            animator.fractionComplete = 0.2
            view = visualEffectView
        }
        
    }
    
    func makeUIViewController(context: UIViewControllerRepresentableContext<UnicornView>) -> UnicornControllerModel {
        return UnicornControllerModel()
    }
    
    func updateUIViewController(_ uiViewController: UnicornControllerModel, context: UIViewControllerRepresentableContext<UnicornView>) {
    }
    
}
#endif
#if os(macOS)
struct UnicornView: NSViewControllerRepresentable {
    
    class UnicornControllerModel: NSViewController {
        
        var animator = NSAnimation(duration: 1, animationCurve: .linear)
        var visualEffectView = NSVisualEffectView()
        
        init() {
            super.init(nibName: nil, bundle: nil)
        }
        required init?(coder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
        override func viewDidLoad() {
            super.viewDidLoad()
            visualEffectView.material = .popover
            visualEffectView.blendingMode = .withinWindow
            view = visualEffectView
        }
        override fun loadView() {
            view = NSView()
        }
        
    }
    
    func makeNSViewController(context: NSViewControllerRepresentableContext<UnicornView>) -> UnicornControllerModel {
        return UnicornControllerModel()
    }
    
    func updateNSViewController(_ nsViewController: UnicornControllerModel, context: NSViewControllerRepresentableContext<UnicornView>) {
    }
    
}
#endif

实现方案

macOS端公开API下没有和iOSfractionComplete1:1对应的属性,可以通过手动控制NSVisualEffectView关联的原生层滤镜参数,实现0~1区间任意调整模糊强度的效果,和iOS端逻辑完全对齐,不需要使用私有API,符合App Store审核要求。
修改后的macOS端UnicornView实现代码如下:

#if os(macOS)
struct UnicornView: NSViewControllerRepresentable {
    // 模糊强度,和iOS端fractionComplete取值范围一致:0~1
    var fractionComplete: CGFloat = 0.2
    
    class UnicornControllerModel: NSViewController {
        var visualEffectView = NSVisualEffectView()
        // 保存系统原生滤镜默认参数,用于进度计算
        private var defaultBlurRadius: CGFloat = 0
        private var defaultSaturation: CGFloat = 0
        var targetFraction: CGFloat = 0.2
        
        override func loadView() {
            view = NSView()
        }
        
        override func viewDidLoad() {
            super.viewDidLoad()
            visualEffectView.material = .popover
            visualEffectView.blendingMode = .withinWindow
            visualEffectView.wantsLayer = true
            visualEffectView.state = .active
            view.addSubview(visualEffectView)
            
            visualEffectView.translatesAutoresizingMaskIntoConstraints = false
            NSLayoutConstraint.activate([
                visualEffectView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
                visualEffectView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
                visualEffectView.topAnchor.constraint(equalTo: view.topAnchor),
                visualEffectView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
            ])
            
            // 视图加载完成后获取系统默认毛玻璃参数
            DispatchQueue.main.async { [weak self] in
                guard let self = self, let filters = self.visualEffectView.layer?.backgroundFilters else { return }
                for filter in filters {
                    if let blur = filter as? CIFilter, blur.name == "CIGaussianBlur" {
                        self.defaultBlurRadius = blur.value(forKey: "inputRadius") as? CGFloat ?? 30
                    }
                    if let sat = filter as? CIFilter, sat.name == "CIColorControls" {
                        self.defaultSaturation = sat.value(forKey: "inputSaturation") as? CGFloat ?? 1.8
                    }
                }
                self.updateBlurIntensity(fraction: self.targetFraction)
            }
        }
        
        func updateBlurIntensity(fraction: CGFloat) {
            guard let filters = visualEffectView.layer?.backgroundFilters else { return }
            let clampedFraction = max(0, min(1, fraction))
            for filter in filters {
                if let blur = filter as? CIFilter, blur.name == "CIGaussianBlur" {
                    blur.setValue(defaultBlurRadius * clampedFraction, forKey: "inputRadius")
                }
                if let sat = filter as? CIFilter, sat.name == "CIColorControls" {
                    // 饱和度随模糊强度同步调整,匹配原生毛玻璃质感
                    let targetSat = 1 + (defaultSaturation - 1) * clampedFraction
                    sat.setValue(targetSat, forKey: "inputSaturation")
                }
            }
        }
    }
    
    func makeNSViewController(context: NSViewControllerRepresentableContext<UnicornView>) -> UnicornControllerModel {
        let controller = UnicornControllerModel()
        controller.targetFraction = fractionComplete
        return controller
    }
    
    func updateNSViewController(_ nsViewController: UnicornControllerModel, context: NSViewControllerRepresentableContext<UnicornView>) {
        nsViewController.updateBlurIntensity(fraction: fractionComplete)
    }
}
#endif

实现说明

  • 逻辑完全对齐iOS端fractionComplete行为,取值范围为0(无模糊效果)到1(系统默认满强度模糊),默认值0.2和原有iOS代码效果一致
  • 直接修改系统原生NSVisualEffectView的层滤镜参数,保留原生毛玻璃的所有视觉特性,不会出现自定义模糊的质感偏差
  • 支持动态调整强度,在SwiftUI中修改传入的fractionComplete值即可实时更新模糊程度,和iOS端调用逻辑一致
  • 所有使用的API均为公开API,无私有API调用风险

内容的提问来源于stack exchange,提问作者swiftPunk

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 10:09:46