Files
2025-12-30 02:05:00 +08:00

179 lines
6.9 KiB
Swift

import Foundation
import UIKit
import Display
import ComponentFlow
import MeshTransform
private let backdropLayerClass: NSObject? = {
let name = ("CA" as NSString).appendingFormat("BackdropLayer")
if let cls = NSClassFromString(name as String) as AnyObject as? NSObject {
return cls
}
return nil
}()
@inline(__always)
private func getMethod<T>(object: NSObject, selector: String) -> T? {
guard let method = object.method(for: NSSelectorFromString(selector)) else {
return nil
}
return unsafeBitCast(method, to: T.self)
}
private var cachedBackdropLayerAllocMethod: (@convention(c) (AnyObject, Selector) -> NSObject?, Selector)?
private func invokeBackdropLayerCreateMethod() -> NSObject? {
guard let backdropLayerClass = backdropLayerClass else {
return nil
}
if let cachedBackdropLayerAllocMethod {
return cachedBackdropLayerAllocMethod.0(backdropLayerClass, cachedBackdropLayerAllocMethod.1)
} else {
let method: (@convention(c) (AnyObject, Selector) -> NSObject?)? = getMethod(object: backdropLayerClass, selector: "alloc")
if let method {
let selector = NSSelectorFromString("alloc")
cachedBackdropLayerAllocMethod = (method, selector)
return method(backdropLayerClass, selector)
} else {
return nil
}
}
}
private var cachedBackdropLayerInitMethod: (@convention(c) (NSObject, Selector) -> NSObject?, Selector)?
private func invokeBackdropLayerInitMethod(object: NSObject) -> NSObject? {
if let cachedBackdropLayerInitMethod {
return cachedBackdropLayerInitMethod.0(object, cachedBackdropLayerInitMethod.1)
} else {
let method: (@convention(c) (AnyObject, Selector) -> NSObject?)? = getMethod(object: object, selector: "init")
if let method {
let selector = NSSelectorFromString("init")
cachedBackdropLayerInitMethod = (method, selector)
return method(object, selector)
} else {
return nil
}
}
}
private var cachedBackdropLayerSetScaleMethod: (@convention(c) (NSObject, Selector, Double) -> Void, Selector)?
private func invokeBackdropLayerSetScaleMethod(object: NSObject, scale: Double) {
if let cachedBackdropLayerSetScaleMethod {
cachedBackdropLayerSetScaleMethod.0(object, cachedBackdropLayerSetScaleMethod.1, scale)
} else {
let method: (@convention(c) (AnyObject, Selector, Double) -> Void)? = getMethod(object: object, selector: "setScale:")
if let method {
let selector = NSSelectorFromString("setScale:")
cachedBackdropLayerSetScaleMethod = (method, selector)
return method(object, selector, scale)
}
}
}
private final class BackdropLayerDelegate: NSObject, CALayerDelegate {
func action(for layer: CALayer, forKey event: String) -> CAAction? {
return nullAction
}
}
final class LegacyGlassView: UIView {
private struct Params: Equatable {
let size: CGSize
let cornerRadius: CGFloat
init(size: CGSize, cornerRadius: CGFloat) {
self.size = size
self.cornerRadius = cornerRadius
}
}
private var params: Params?
private let backdropLayer: CALayer?
private let backdropLayerDelegate: BackdropLayerDelegate
override init(frame: CGRect) {
self.backdropLayerDelegate = BackdropLayerDelegate()
self.backdropLayer = invokeBackdropLayerCreateMethod().flatMap(invokeBackdropLayerInitMethod) as? CALayer
super.init(frame: frame)
self.layer.cornerCurve = .circular
self.clipsToBounds = true
if let backdropLayer = self.backdropLayer {
self.layer.addSublayer(backdropLayer)
backdropLayer.delegate = self.backdropLayerDelegate
let blur: CGFloat
let scale: CGFloat
blur = 2.0
scale = 1.0
invokeBackdropLayerSetScaleMethod(object: backdropLayer, scale: scale)
backdropLayer.rasterizationScale = scale
if let blurFilter = CALayer.blur() {
blurFilter.setValue(blur as NSNumber, forKey: "inputRadius")
backdropLayer.filters = [blurFilter]
}
}
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func update(size: CGSize, cornerRadius: CGFloat, transition: ComponentTransition) {
let params = Params(size: size, cornerRadius: cornerRadius)
if self.params == params {
return
}
self.params = params
guard let backdropLayer = self.backdropLayer else {
return
}
transition.setCornerRadius(layer: self.layer, cornerRadius: cornerRadius)
transition.setFrame(layer: backdropLayer, frame: CGRect(origin: CGPoint(), size: size))
if !"".isEmpty {
let size = CGSize(width: max(1.0, size.width), height: max(1.0, size.height))
let cornerRadius = min(min(size.width, size.height) * 0.5, cornerRadius)
let displacementMagnitudePoints: CGFloat = 20.0
let displacementMagnitudeU = displacementMagnitudePoints / size.width
let displacementMagnitudeV = displacementMagnitudePoints / size.height
let outerEdgeDistance = 2.0
if let displacementMap = generateDisplacementMap(size: size, cornerRadius: cornerRadius, edgeDistance: min(12.0, cornerRadius), scale: 1.0) {
let meshTransform = generateGlassMeshFromDisplacementMap(
size: size,
cornerRadius: cornerRadius,
displacementMap: displacementMap,
displacementMagnitudeU: displacementMagnitudeU,
displacementMagnitudeV: displacementMagnitudeV,
cornerResolution: 12,
outerEdgeDistance: outerEdgeDistance,
bezier: DisplacementBezier(
x1: 0.816137566137566,
y1: 0.20502645502645533,
x2: 0.5806878306878306,
y2: 0.873015873015873
)
).mesh.makeValue()
if let meshTransform {
if !transition.animation.isImmediate, let previousTransform = backdropLayer.value(forKey: "meshTransform") as? NSObject {
backdropLayer.removeAnimation(forKey: "meshTransform")
backdropLayer.setValue(meshTransform, forKey: "meshTransform")
transition.animateMeshTransform(layer: backdropLayer, from: previousTransform, to: meshTransform)
} else {
backdropLayer.setValue(meshTransform, forKey: "meshTransform")
}
}
}
}
}
}