mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2026-02-10 04:56:22 +00:00
333 lines
14 KiB
Swift
333 lines
14 KiB
Swift
import Foundation
|
|
import UIKit
|
|
import AsyncDisplayKit
|
|
|
|
private var sharedIsReduceTransparencyEnabled = UIAccessibility.isReduceTransparencyEnabled
|
|
|
|
public final class NavigationBackgroundNode: ASDisplayNode {
|
|
private var _color: UIColor
|
|
|
|
public var color: UIColor {
|
|
return self._color
|
|
}
|
|
|
|
private var enableBlur: Bool
|
|
private var enableSaturation: Bool
|
|
private var customBlurRadius: CGFloat?
|
|
|
|
public var effectView: UIVisualEffectView?
|
|
private let backgroundNode: ASDisplayNode
|
|
|
|
public var backgroundView: UIView {
|
|
return self.backgroundNode.view
|
|
}
|
|
|
|
private var validLayout: (CGSize, CGFloat)?
|
|
|
|
public var backgroundCornerRadius: CGFloat {
|
|
if let (_, cornerRadius) = self.validLayout {
|
|
return cornerRadius
|
|
} else {
|
|
return 0.0
|
|
}
|
|
}
|
|
|
|
public init(color: UIColor, enableBlur: Bool = true, enableSaturation: Bool = true, customBlurRadius: CGFloat? = nil) {
|
|
self._color = .clear
|
|
self.enableBlur = enableBlur
|
|
self.enableSaturation = enableSaturation
|
|
self.customBlurRadius = customBlurRadius
|
|
|
|
self.backgroundNode = ASDisplayNode()
|
|
|
|
super.init()
|
|
|
|
self.addSubnode(self.backgroundNode)
|
|
|
|
self.updateColor(color: color, transition: .immediate)
|
|
}
|
|
|
|
|
|
public override func didLoad() {
|
|
super.didLoad()
|
|
|
|
if self.scheduledUpdate {
|
|
self.scheduledUpdate = false
|
|
self.updateBackgroundBlur(forceKeepBlur: false)
|
|
}
|
|
}
|
|
|
|
private var scheduledUpdate = false
|
|
|
|
private func updateBackgroundBlur(forceKeepBlur: Bool) {
|
|
guard self.isNodeLoaded else {
|
|
self.scheduledUpdate = true
|
|
return
|
|
}
|
|
if self.enableBlur && !sharedIsReduceTransparencyEnabled && ((self._color.alpha > .ulpOfOne && self._color.alpha < 0.95) || forceKeepBlur) {
|
|
if self.effectView == nil {
|
|
let effectView = UIVisualEffectView(effect: UIBlurEffect(style: .light))
|
|
|
|
for subview in effectView.subviews {
|
|
if subview.description.contains("VisualEffectSubview") {
|
|
subview.isHidden = true
|
|
}
|
|
}
|
|
|
|
if let sublayer = effectView.layer.sublayers?[0], let filters = sublayer.filters {
|
|
sublayer.backgroundColor = nil
|
|
sublayer.isOpaque = false
|
|
var allowedKeys: [String] = [
|
|
"gaussianBlur"
|
|
]
|
|
if self.enableSaturation {
|
|
allowedKeys.append("colorSaturate")
|
|
}
|
|
sublayer.filters = filters.filter { filter in
|
|
guard let filter = filter as? NSObject else {
|
|
return true
|
|
}
|
|
let filterName = String(describing: filter)
|
|
if !allowedKeys.contains(filterName) {
|
|
return false
|
|
}
|
|
if let customBlurRadius = self.customBlurRadius, filterName == "gaussianBlur" {
|
|
filter.setValue(customBlurRadius as NSNumber, forKey: "inputRadius")
|
|
}
|
|
return true
|
|
}
|
|
}
|
|
|
|
if let (size, cornerRadius) = self.validLayout {
|
|
effectView.frame = CGRect(origin: CGPoint(), size: size)
|
|
ContainedViewLayoutTransition.immediate.updateCornerRadius(layer: effectView.layer, cornerRadius: cornerRadius)
|
|
effectView.clipsToBounds = !cornerRadius.isZero
|
|
}
|
|
self.effectView = effectView
|
|
self.view.insertSubview(effectView, at: 0)
|
|
}
|
|
} else if let effectView = self.effectView {
|
|
self.effectView = nil
|
|
effectView.removeFromSuperview()
|
|
}
|
|
}
|
|
|
|
public func updateColor(color: UIColor, enableBlur: Bool? = nil, enableSaturation: Bool? = nil, forceKeepBlur: Bool = false, transition: ContainedViewLayoutTransition) {
|
|
let effectiveEnableBlur = enableBlur ?? self.enableBlur
|
|
let effectiveEnableSaturation = enableSaturation ?? self.enableSaturation
|
|
|
|
if self._color.isEqual(color) && self.enableBlur == effectiveEnableBlur && self.enableSaturation == effectiveEnableSaturation {
|
|
return
|
|
}
|
|
self._color = color
|
|
self.enableBlur = effectiveEnableBlur
|
|
self.enableSaturation = effectiveEnableSaturation
|
|
|
|
if sharedIsReduceTransparencyEnabled {
|
|
transition.updateBackgroundColor(node: self.backgroundNode, color: self._color.withAlphaComponent(1.0))
|
|
} else {
|
|
transition.updateBackgroundColor(node: self.backgroundNode, color: self._color)
|
|
}
|
|
|
|
self.updateBackgroundBlur(forceKeepBlur: forceKeepBlur)
|
|
}
|
|
|
|
public func update(size: CGSize, cornerRadius: CGFloat = 0.0, transition: ContainedViewLayoutTransition, beginWithCurrentState: Bool = true) {
|
|
self.validLayout = (size, cornerRadius)
|
|
|
|
let contentFrame = CGRect(origin: CGPoint(), size: size)
|
|
transition.updateFrame(node: self.backgroundNode, frame: contentFrame, beginWithCurrentState: true)
|
|
if let effectView = self.effectView, effectView.frame != contentFrame {
|
|
transition.updateFrame(layer: effectView.layer, frame: contentFrame, beginWithCurrentState: true)
|
|
if let sublayers = effectView.layer.sublayers {
|
|
for sublayer in sublayers {
|
|
transition.updateFrame(layer: sublayer, frame: contentFrame, beginWithCurrentState: true)
|
|
}
|
|
}
|
|
}
|
|
|
|
transition.updateCornerRadius(node: self.backgroundNode, cornerRadius: cornerRadius)
|
|
if let effectView = self.effectView {
|
|
transition.updateCornerRadius(layer: effectView.layer, cornerRadius: cornerRadius)
|
|
effectView.clipsToBounds = !cornerRadius.isZero
|
|
}
|
|
}
|
|
|
|
public func update(size: CGSize, cornerRadius: CGFloat = 0.0, animator: ControlledTransitionAnimator) {
|
|
self.validLayout = (size, cornerRadius)
|
|
|
|
let contentFrame = CGRect(origin: CGPoint(), size: size)
|
|
animator.updateFrame(layer: self.backgroundNode.layer, frame: contentFrame, completion: nil)
|
|
if let effectView = self.effectView, effectView.frame != contentFrame {
|
|
animator.updateFrame(layer: effectView.layer, frame: contentFrame, completion: nil)
|
|
if let sublayers = effectView.layer.sublayers {
|
|
for sublayer in sublayers {
|
|
animator.updateFrame(layer: sublayer, frame: contentFrame, completion: nil)
|
|
}
|
|
}
|
|
}
|
|
|
|
animator.updateCornerRadius(layer: self.backgroundNode.layer, cornerRadius: cornerRadius, completion: nil)
|
|
if let effectView = self.effectView {
|
|
animator.updateCornerRadius(layer: effectView.layer, cornerRadius: cornerRadius, completion: nil)
|
|
effectView.clipsToBounds = !cornerRadius.isZero
|
|
}
|
|
}
|
|
}
|
|
|
|
open class BlurredBackgroundView: UIView {
|
|
private var _color: UIColor?
|
|
|
|
private var enableBlur: Bool
|
|
private var customBlurRadius: CGFloat?
|
|
|
|
public private(set) var effectView: UIVisualEffectView?
|
|
private let backgroundView: UIView
|
|
|
|
private var validLayout: (CGSize, CGFloat)?
|
|
|
|
public var backgroundCornerRadius: CGFloat {
|
|
if let (_, cornerRadius) = self.validLayout {
|
|
return cornerRadius
|
|
} else {
|
|
return 0.0
|
|
}
|
|
}
|
|
|
|
public init(color: UIColor?, enableBlur: Bool = true, customBlurRadius: CGFloat? = nil) {
|
|
self._color = nil
|
|
self.enableBlur = enableBlur
|
|
self.customBlurRadius = customBlurRadius
|
|
|
|
self.backgroundView = UIView()
|
|
|
|
super.init(frame: CGRect())
|
|
|
|
self.addSubview(self.backgroundView)
|
|
|
|
if let color = color {
|
|
self.updateColor(color: color, transition: .immediate)
|
|
}
|
|
}
|
|
|
|
required public init?(coder: NSCoder) {
|
|
fatalError("init(coder:) has not been implemented")
|
|
}
|
|
|
|
private func updateBackgroundBlur(forceKeepBlur: Bool) {
|
|
if let color = self._color, self.enableBlur && !sharedIsReduceTransparencyEnabled && ((color.alpha > .ulpOfOne && color.alpha < 0.95) || forceKeepBlur) {
|
|
if self.effectView == nil {
|
|
let effectView = UIVisualEffectView(effect: UIBlurEffect(style: .light))
|
|
|
|
for subview in effectView.subviews {
|
|
if subview.description.contains("VisualEffectSubview") {
|
|
subview.isHidden = true
|
|
}
|
|
}
|
|
|
|
if let sublayer = effectView.layer.sublayers?[0], let filters = sublayer.filters {
|
|
sublayer.backgroundColor = nil
|
|
sublayer.isOpaque = false
|
|
//sublayer.setValue(true as NSNumber, forKey: "allowsInPlaceFiltering")
|
|
let allowedKeys: [String] = [
|
|
"colorSaturate",
|
|
"gaussianBlur"
|
|
]
|
|
sublayer.filters = filters.filter { filter in
|
|
guard let filter = filter as? NSObject else {
|
|
return true
|
|
}
|
|
let filterName = String(describing: filter)
|
|
if !allowedKeys.contains(filterName) {
|
|
return false
|
|
}
|
|
if let customBlurRadius = self.customBlurRadius, filterName == "gaussianBlur" {
|
|
filter.setValue(customBlurRadius as NSNumber, forKey: "inputRadius")
|
|
}
|
|
return true
|
|
}
|
|
}
|
|
|
|
if let (size, cornerRadius) = self.validLayout {
|
|
effectView.frame = CGRect(origin: CGPoint(), size: size)
|
|
ContainedViewLayoutTransition.immediate.updateCornerRadius(layer: effectView.layer, cornerRadius: cornerRadius)
|
|
effectView.clipsToBounds = !cornerRadius.isZero
|
|
}
|
|
self.effectView = effectView
|
|
self.insertSubview(effectView, at: 0)
|
|
}
|
|
} else if let effectView = self.effectView {
|
|
self.effectView = nil
|
|
effectView.removeFromSuperview()
|
|
}
|
|
}
|
|
|
|
public func updateColor(color: UIColor, enableBlur: Bool? = nil, forceKeepBlur: Bool = false, transition: ContainedViewLayoutTransition) {
|
|
let effectiveEnableBlur = enableBlur ?? self.enableBlur
|
|
|
|
if self._color == color && self.enableBlur == effectiveEnableBlur {
|
|
return
|
|
}
|
|
self._color = color
|
|
self.enableBlur = effectiveEnableBlur
|
|
|
|
if sharedIsReduceTransparencyEnabled {
|
|
transition.updateBackgroundColor(layer: self.backgroundView.layer, color: color.withAlphaComponent(1.0))
|
|
} else {
|
|
transition.updateBackgroundColor(layer: self.backgroundView.layer, color: color)
|
|
}
|
|
|
|
self.updateBackgroundBlur(forceKeepBlur: forceKeepBlur)
|
|
}
|
|
|
|
public func update(size: CGSize, cornerRadius: CGFloat = 0.0, maskedCorners: CACornerMask = [.layerMaxXMaxYCorner, .layerMaxXMinYCorner, .layerMinXMaxYCorner, .layerMinXMinYCorner], transition: ContainedViewLayoutTransition) {
|
|
self.validLayout = (size, cornerRadius)
|
|
|
|
let contentFrame = CGRect(origin: CGPoint(), size: size)
|
|
transition.updateFrame(view: self.backgroundView, frame: contentFrame, beginWithCurrentState: true)
|
|
if let effectView = self.effectView, effectView.frame != contentFrame {
|
|
transition.updateFrame(layer: effectView.layer, frame: contentFrame, beginWithCurrentState: true)
|
|
if let sublayers = effectView.layer.sublayers {
|
|
for sublayer in sublayers {
|
|
transition.updateFrame(layer: sublayer, frame: contentFrame, beginWithCurrentState: true)
|
|
}
|
|
}
|
|
}
|
|
|
|
if #available(iOS 11.0, *) {
|
|
self.backgroundView.layer.maskedCorners = maskedCorners
|
|
}
|
|
|
|
transition.updateCornerRadius(layer: self.backgroundView.layer, cornerRadius: cornerRadius)
|
|
if let effectView = self.effectView {
|
|
transition.updateCornerRadius(layer: effectView.layer, cornerRadius: cornerRadius)
|
|
effectView.clipsToBounds = !cornerRadius.isZero
|
|
|
|
if #available(iOS 11.0, *) {
|
|
effectView.layer.maskedCorners = maskedCorners
|
|
}
|
|
}
|
|
}
|
|
|
|
public func update(size: CGSize, cornerRadius: CGFloat = 0.0, animator: ControlledTransitionAnimator) {
|
|
self.validLayout = (size, cornerRadius)
|
|
|
|
let contentFrame = CGRect(origin: CGPoint(), size: size)
|
|
animator.updateFrame(layer: self.backgroundView.layer, frame: contentFrame, completion: nil)
|
|
if let effectView = self.effectView, effectView.frame != contentFrame {
|
|
animator.updateFrame(layer: effectView.layer, frame: contentFrame, completion: nil)
|
|
if let sublayers = effectView.layer.sublayers {
|
|
for sublayer in sublayers {
|
|
animator.updateFrame(layer: sublayer, frame: contentFrame, completion: nil)
|
|
}
|
|
}
|
|
}
|
|
|
|
animator.updateCornerRadius(layer: self.backgroundView.layer, cornerRadius: cornerRadius, completion: nil)
|
|
if let effectView = self.effectView {
|
|
animator.updateCornerRadius(layer: effectView.layer, cornerRadius: cornerRadius, completion: nil)
|
|
effectView.clipsToBounds = !cornerRadius.isZero
|
|
}
|
|
}
|
|
}
|