mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-11-29 03:21:29 +00:00
212 lines
7.6 KiB
Swift
212 lines
7.6 KiB
Swift
import Foundation
|
|
import UIKit
|
|
import Display
|
|
import ComponentFlow
|
|
|
|
public final class EdgeEffectView: UIView {
|
|
public enum Edge {
|
|
case top
|
|
case bottom
|
|
}
|
|
|
|
private let contentView: UIView
|
|
private let contentMaskView: UIImageView
|
|
private var blurView: VariableBlurView?
|
|
|
|
public override init(frame: CGRect) {
|
|
self.contentView = UIView()
|
|
self.contentMaskView = UIImageView()
|
|
self.contentView.mask = self.contentMaskView
|
|
|
|
super.init(frame: frame)
|
|
|
|
self.addSubview(self.contentView)
|
|
}
|
|
|
|
required public init?(coder: NSCoder) {
|
|
fatalError("init(coder:) has not been implemented")
|
|
}
|
|
|
|
public func update(content: UIColor, blur: Bool = false, alpha: CGFloat = 0.65, rect: CGRect, edge: Edge, edgeSize: CGFloat, transition: ComponentTransition) {
|
|
transition.setBackgroundColor(view: self.contentView, color: content)
|
|
|
|
switch edge {
|
|
case .top:
|
|
self.contentMaskView.transform = CGAffineTransformMakeScale(1.0, -1.0)
|
|
case .bottom:
|
|
self.contentMaskView.transform = .identity
|
|
}
|
|
|
|
let bounds = CGRect(origin: CGPoint(), size: rect.size)
|
|
transition.setFrame(view: self.contentView, frame: bounds)
|
|
transition.setFrame(view: self.contentMaskView, frame: bounds)
|
|
|
|
if self.contentMaskView.image?.size.height != edgeSize {
|
|
let baseGradientAlpha: CGFloat = alpha
|
|
let numSteps = 8
|
|
let firstStep = 1
|
|
let firstLocation = 0.0
|
|
let colors: [UIColor] = (0 ..< numSteps).map { i in
|
|
if i < firstStep {
|
|
return UIColor(white: 1.0, alpha: 1.0)
|
|
} else {
|
|
let step: CGFloat = CGFloat(i - firstStep) / CGFloat(numSteps - firstStep - 1)
|
|
let value: CGFloat = bezierPoint(0.42, 0.0, 0.58, 1.0, step)
|
|
return UIColor(white: 1.0, alpha: baseGradientAlpha * value)
|
|
}
|
|
}
|
|
let locations: [CGFloat] = (0 ..< numSteps).map { i in
|
|
if i < firstStep {
|
|
return 0.0
|
|
} else {
|
|
let step: CGFloat = CGFloat(i - firstStep) / CGFloat(numSteps - firstStep - 1)
|
|
return (firstLocation + (1.0 - firstLocation) * step)
|
|
}
|
|
}
|
|
|
|
if edgeSize > 0.0 {
|
|
self.contentMaskView.image = generateGradientImage(
|
|
size: CGSize(width: 8.0, height: edgeSize),
|
|
colors: colors,
|
|
locations: locations
|
|
)?.stretchableImage(withLeftCapWidth: 0, topCapHeight: Int(edgeSize))
|
|
} else {
|
|
self.contentMaskView.image = nil
|
|
}
|
|
}
|
|
|
|
if blur {
|
|
let gradientMaskLayer = SimpleGradientLayer()
|
|
let baseGradientAlpha: CGFloat = 1.0
|
|
let numSteps = 8
|
|
let firstStep = 1
|
|
let firstLocation = 0.8
|
|
gradientMaskLayer.colors = (0 ..< numSteps).map { i in
|
|
if i < firstStep {
|
|
return UIColor(white: 1.0, alpha: 1.0).cgColor
|
|
} else {
|
|
let step: CGFloat = CGFloat(i - firstStep) / CGFloat(numSteps - firstStep - 1)
|
|
let value: CGFloat = 1.0 - bezierPoint(0.42, 0.0, 0.58, 1.0, step)
|
|
return UIColor(white: 1.0, alpha: baseGradientAlpha * value).cgColor
|
|
}
|
|
}
|
|
gradientMaskLayer.locations = (0 ..< numSteps).map { i -> NSNumber in
|
|
if i < firstStep {
|
|
return 0.0 as NSNumber
|
|
} else {
|
|
let step: CGFloat = CGFloat(i - firstStep) / CGFloat(numSteps - firstStep - 1)
|
|
return (firstLocation + (1.0 - firstLocation) * step) as NSNumber
|
|
}
|
|
}
|
|
|
|
let blurView: VariableBlurView
|
|
if let current = self.blurView {
|
|
blurView = current
|
|
} else {
|
|
blurView = VariableBlurView(gradientMask: self.contentMaskView.image ?? UIImage(), maxBlurRadius: 8.0)
|
|
blurView.layer.mask = gradientMaskLayer
|
|
self.insertSubview(blurView, at: 0)
|
|
self.blurView = blurView
|
|
}
|
|
transition.setFrame(view: blurView, frame: bounds)
|
|
if let maskLayer = blurView.layer.mask {
|
|
transition.setFrame(layer: maskLayer, frame: bounds)
|
|
maskLayer.transform = CATransform3DMakeScale(1.0, -1.0, 1.0)
|
|
}
|
|
blurView.transform = self.contentMaskView.transform
|
|
} else if let blurView = self.blurView {
|
|
self.blurView = nil
|
|
blurView.removeFromSuperview()
|
|
}
|
|
}
|
|
}
|
|
|
|
public final class VariableBlurView: UIVisualEffectView {
|
|
public let maxBlurRadius: CGFloat
|
|
|
|
public var gradientMask: UIImage {
|
|
didSet {
|
|
if self.gradientMask !== oldValue {
|
|
self.resetEffect()
|
|
}
|
|
}
|
|
}
|
|
|
|
public init(gradientMask: UIImage, maxBlurRadius: CGFloat = 20.0) {
|
|
self.gradientMask = gradientMask
|
|
self.maxBlurRadius = maxBlurRadius
|
|
|
|
super.init(effect: UIBlurEffect(style: .regular))
|
|
|
|
self.resetEffect()
|
|
|
|
if self.subviews.indices.contains(1) {
|
|
let tintOverlayView = subviews[1]
|
|
tintOverlayView.alpha = 0
|
|
}
|
|
}
|
|
|
|
required public init?(coder: NSCoder) {
|
|
fatalError("init(coder:) has not been implemented")
|
|
}
|
|
|
|
override public func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
|
|
if self.traitCollection.hasDifferentColorAppearance(comparedTo: previousTraitCollection) {
|
|
self.resetEffect()
|
|
}
|
|
}
|
|
|
|
private func resetEffect() {
|
|
let filterClassStringEncoded = "Q0FGaWx0ZXI="
|
|
let filterClassString: String = {
|
|
if
|
|
let data = Data(base64Encoded: filterClassStringEncoded),
|
|
let string = String(data: data, encoding: .utf8)
|
|
{
|
|
return string
|
|
}
|
|
|
|
return ""
|
|
}()
|
|
let filterWithTypeStringEncoded = "ZmlsdGVyV2l0aFR5cGU6"
|
|
let filterWithTypeString: String = {
|
|
if
|
|
let data = Data(base64Encoded: filterWithTypeStringEncoded),
|
|
let string = String(data: data, encoding: .utf8)
|
|
{
|
|
return string
|
|
}
|
|
|
|
return ""
|
|
}()
|
|
|
|
let filterWithTypeSelector = Selector(filterWithTypeString)
|
|
|
|
guard let filterClass = NSClassFromString(filterClassString) as AnyObject as? NSObjectProtocol else {
|
|
return
|
|
}
|
|
|
|
guard filterClass.responds(to: filterWithTypeSelector) else {
|
|
return
|
|
}
|
|
|
|
let variableBlur = filterClass.perform(filterWithTypeSelector, with: "variableBlur").takeUnretainedValue()
|
|
|
|
guard let variableBlur = variableBlur as? NSObject else {
|
|
return
|
|
}
|
|
|
|
guard let gradientImageRef = self.gradientMask.cgImage else {
|
|
return
|
|
}
|
|
|
|
variableBlur.setValue(self.maxBlurRadius, forKey: "inputRadius")
|
|
variableBlur.setValue(gradientImageRef, forKey: "inputMaskImage")
|
|
variableBlur.setValue(true, forKey: "inputNormalizeEdges")
|
|
|
|
let backdropLayer = self.subviews.first?.layer
|
|
backdropLayer?.filters = [variableBlur]
|
|
backdropLayer?.setValue(UIScreenScale, forKey: "scale")
|
|
}
|
|
}
|