check
//
// BlurEffectView.swift
// KakaduKit
//
// Created by Игорь Чикичев on 31.03.2025.
//
import SwiftUI
import QuartzCore
import UIKit
/*
Использование:
BlurEffectViewRepresentable(style: .systemMaterial, intensity: 0.5)
*/
// MARK: - SwiftUI Обёртка
public struct BlurEffectViewRepresentable: UIViewRepresentable {
public var style: UIBlurEffect.Style
@Binding var intensity: CGFloat
public init(style: UIBlurEffect.Style, intensity: Binding<CGFloat>) {
self.style = style
self._intensity = intensity
}
public func makeUIView(context: Context) -> BlurEffectView {
let view = BlurEffectView()
view.effect = UIBlurEffect(style: style)
view.intensity = intensity
return view
}
public func updateUIView(_ uiView: BlurEffectView, context: Context) {
uiView.effect = UIBlurEffect(style: style)
uiView.intensity = intensity
}
}
public class BlurIntensityLayer: CALayer {
@NSManaged var intensity: CGFloat
override init(layer: Any) {
super.init(layer: layer)
if let layer = layer as? BlurIntensityLayer {
self.intensity = layer.intensity
}
}
override init() {
super.init()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
public override class func needsDisplay(forKey key: String) -> Bool {
return key == #keyPath(intensity) || super.needsDisplay(forKey: key)
}
public override func action(forKey event: String) -> CAAction? {
guard event == #keyPath(intensity) else {
return super.action(forKey: event)
}
let animation = CABasicAnimation(keyPath: event)
animation.fromValue = (self.presentation() ?? self).intensity
return animation
}
}
public class BlurEffectView: UIView {
public override class var layerClass: AnyClass {
return BlurIntensityLayer.self
}
@objc
@IBInspectable
public dynamic var intensity: CGFloat {
set { self.blurIntensityLayer.intensity = newValue }
get { return self.blurIntensityLayer.intensity }
}
@IBInspectable
public var effect = UIBlurEffect(style: .dark) {
didSet {
self.setupPropertyAnimator()
}
}
private let visualEffectView = UIVisualEffectView(effect: nil)
private var propertyAnimator: UIViewPropertyAnimator!
private var blurIntensityLayer: BlurIntensityLayer {
return self.layer as! BlurIntensityLayer
}
override init(frame: CGRect) {
super.init(frame: frame)
self.setupView()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
self.setupView()
}
deinit {
guard let animator = propertyAnimator else { return }
DispatchQueue.main.async {
animator.stopAnimation(true)
}
}
private func setupPropertyAnimator() {
self.propertyAnimator?.stopAnimation(true)
self.propertyAnimator = UIViewPropertyAnimator(duration: 1, curve: .linear)
self.propertyAnimator.addAnimations { [weak self] in
self?.visualEffectView.effect = self?.effect
}
self.propertyAnimator.pausesOnCompletion = true
}
private func setupView() {
self.backgroundColor = .clear
self.isUserInteractionEnabled = false
self.addSubview(self.visualEffectView)
self.visualEffectView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
visualEffectView.topAnchor.constraint(equalTo: topAnchor),
visualEffectView.bottomAnchor.constraint(equalTo: bottomAnchor),
visualEffectView.leadingAnchor.constraint(equalTo: leadingAnchor),
visualEffectView.trailingAnchor.constraint(equalTo: trailingAnchor)
])
self.setupPropertyAnimator()
}
public override func display(_ layer: CALayer) {
guard let presentationLayer = layer.presentation() as? BlurIntensityLayer else {
return
}
let clampedIntensity = max(0.0, min(1.0, presentationLayer.intensity))
self.propertyAnimator.fractionComplete = clampedIntensity
}
}
Topic:
UI Frameworks
SubTopic:
SwiftUI
Tags: