import Foundation import UIKit import SwiftSignalKit import AsyncDisplayKit import Accelerate import ImageBlur private class BlurLayer: CALayer { private static let blurRadiusKey = "blurRadius" @NSManaged var blurRadius: CGFloat private var fromBlurRadius: CGFloat? var presentationRadius: CGFloat { if let radius = self.fromBlurRadius { if let layer = presentation() { return layer.blurRadius } else { return radius } } else { return self.blurRadius } } override class func needsDisplay(forKey key: String) -> Bool { if key == blurRadiusKey { return true } return super.needsDisplay(forKey: key) } open override func action(forKey event: String) -> CAAction? { if event == BlurLayer.blurRadiusKey { self.fromBlurRadius = nil if let action = super.action(forKey: "opacity") as? CABasicAnimation { self.fromBlurRadius = (presentation() ?? self).blurRadius action.keyPath = event action.fromValue = self.fromBlurRadius return action } } return super.action(forKey: event) } func draw(_ image: UIImage) { self.contents = image.cgImage self.contentsScale = image.scale self.contentsGravity = .resizeAspectFill } func render(in context: CGContext, for layer: CALayer) { layer.render(in: context) } } public class BlurView: UIView { public override class var layerClass : AnyClass { return BlurLayer.self } private var blurLayer: BlurLayer { return self.layer as! BlurLayer } var image: UIImage? private let queue: Queue = { return Queue(name: nil, qos: .userInteractive) }() public var blurRadius: CGFloat { set { self.blurLayer.blurRadius = newValue } get { return self.blurLayer.blurRadius } } public override init(frame: CGRect) { super.init(frame: frame) self.isUserInteractionEnabled = false } public required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) self.isUserInteractionEnabled = false } private func async(on queue: DispatchQueue, actions: @escaping () -> Void) { queue.async(execute: actions) } private func sync(on queue: DispatchQueue, actions: () -> Void) { queue.sync(execute: actions) } private func draw(_ image: UIImage, blurRadius: CGFloat) { self.queue.async { [weak self] in if let strongSelf = self, let blurredImage = blurredImage(image, radius: blurRadius) { Queue.mainQueue().sync { strongSelf.blurLayer.draw(blurredImage) } } } } public override func display(_ layer: CALayer) { let blurRadius = self.blurLayer.presentationRadius if let image = self.image { self.draw(image, blurRadius: blurRadius) } } } public final class BlurredImageNode: ASDisplayNode { public var image: UIImage? { didSet { self.blurView.image = self.image self.blurView.layer.setNeedsDisplay() } } public var blurView: BlurView { return (self.view as? BlurView)! } public override init() { super.init() self.setViewBlock({ return BlurView() }) } }