Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update the library #43

Closed
yura-hb opened this issue Aug 12, 2024 · 2 comments
Closed

Update the library #43

yura-hb opened this issue Aug 12, 2024 · 2 comments

Comments

@yura-hb
Copy link

yura-hb commented Aug 12, 2024

Hi,

the issue is created to mainly address the issues of the library post iOS 14, while these issues in general can be addressed by creating the fork and correcting them there. I still believe that it is better to address it here.


Firstly, I would like to address that the library should really split its implementations for the iOS 9 and iOS 14. It is because the implementation loses clarity. For instance, the library creates, manages and updates _UICustomBlurEffect which does not affect anything on iOS 14.

/// Returns the instance of UIBlurEffect.
private let blurEffect = (NSClassFromString("_UICustomBlurEffect") as! UIBlurEffect.Type).init()

Mainly due to the first issue, input variables aren't clear to end user. For instance, colorTint is sufficient post iOS 14, because it directly uses alpha from UIColor, e.g.

ios14_colorTint = newValue
ios14_colorTint = ios14_colorTint?.withAlphaComponent(newValue)

At the same time, scale doesn't have any effect and still it is present in parameters of the SwiftUI view.


Finally, the view automatically increases the saturation, which has appeared in several issues. It can be observed by accessing backdropView?.value(forKey: "filters") which is

Optional(<__NSArrayI 0x600000353380>(
<_UIVisualEffectFilterEntry 0x6000021b2d00>: filter=gaussianBlur parameters={(inputQuality=default), (inputNormalizeEdges=1), (inputRadius=[0][16])} scale=[1.000000][1.000000],
<_UIVisualEffectFilterEntry 0x6000021b2d50>: filter=colorSaturate parameters={(inputAmount=[1][1.8])}
)

By setting value of inputAmount to 1, the blur view wouldn't oversaturate background and VisualEffectView would behave as actual blur.

Thanks.

P.S. For instance, the implementation can be reduced to

Reduced implementation without saturation
private struct VisualEffect: UIViewRepresentable {
    let colorTint: UIColor?
    let blurRadius: CGFloat
  
    public init(colorTint: UIColor? = nil, blurRadius: CGFloat = 0) {
      self.colorTint = colorTint
      self.blurRadius = blurRadius
    }
  
    public func makeUIView(context: Context) -> VisualEffectView {
      let view = VisualEffectView()
  
      view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
  
      if let colorTint {
        view.colorTint = colorTint
      }
  
      view.blurRadius = blurRadius
  
      return view
    }
  
    public func updateUIView(_ uiView: VisualEffectView, context: Context) {
      if let colorTint {
        uiView.colorTint = colorTint
      }
  
      uiView.blurRadius = blurRadius
    }
  }
  
  private class VisualEffectView: UIVisualEffectView {
  
    open var colorTint: UIColor? {
      get {
        sourceOver?.value(forKeyPath: "color") as? UIColor
      }
      set {
        prepareForChanges()
        sourceOver?.setValue(newValue, forKeyPath: "color")
        sourceOver?.perform(Selector(("applyRequestedEffectToView:")), with: overlayView)
        applyChanges()
        overlayView?.backgroundColor = newValue
      }
    }
  
    open var blurRadius: CGFloat {
      get {
        gaussianBlur?.requestedValues?["inputRadius"] as? CGFloat ?? 0
      }
      set {
        prepareForChanges()
        gaussianBlur?.requestedValues?["inputRadius"] = newValue
        // Control saturation
        colorSaturate?.requestedValues?["inputAmount"] = 1.0
        applyChanges()
      }
    }
  
    public override init(effect: UIVisualEffect?) {
      super.init(effect: effect)
    }
  
    required init?(coder: NSCoder) {
      fatalError("init(coder:) has not been implemented")
    }
    
  }
  
  private extension UIVisualEffectView {
  
    var backdropView: UIView? {
      subview(of: NSClassFromString("_UIVisualEffectBackdropView"))
    }
  
    var overlayView: UIView? {
      subview(of: NSClassFromString("_UIVisualEffectSubview"))
    }
  
    var gaussianBlur: NSObject? {
      backdropView?.value(forKey: "filters", withFilterType: "gaussianBlur")
    }
  
    var colorSaturate: NSObject? {
      backdropView?.value(forKey: "filters", withFilterType: "colorSaturate")
    }
  
    var sourceOver: NSObject? {
      overlayView?.value(forKey: "viewEffects", withFilterType: "sourceOver")
    }
  
    func prepareForChanges() {
      self.effect = UIBlurEffect(style: .light)
  
      gaussianBlur?.setValue(1.0, forKeyPath: "requestedScaleHint")
    }
  
    func applyChanges() {
      backdropView?.perform(Selector(("applyRequestedFilterEffects")))
    }
  
  }
  
  private extension NSObject {
  
    var requestedValues: [String: Any]? {
      get { value(forKeyPath: "requestedValues") as? [String: Any] }
      set { setValue(newValue, forKeyPath: "requestedValues") }
    }
  
    func value(forKey key: String, withFilterType filterType: String) -> NSObject? {
      (value(forKeyPath: key) as? [NSObject])?.first { $0.value(forKeyPath: "filterType") as? String == filterType }
    }
  
  }
  
  private extension UIView {
  
    func subview(of classType: AnyClass?) -> UIView? {
      subviews.first { type(of: $0) == classType }
    }
  
  }
@efremidze
Copy link
Owner

I agree, I'll push a fix

@efremidze
Copy link
Owner

Resolved by #44

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants