mirror of
https://github.com/ichmagmaus111/ghostgram.git
synced 2026-07-07 16:47:48 +02:00
chore: migrate to new version + fixed several critical bugs
- Migrated project to latest Telegram iOS base (v12.3.2+) - Fixed circular dependency between GhostModeManager and MiscSettingsManager - Fixed multiple Bazel build configuration errors (select() default conditions) - Fixed duplicate type definitions in PeerInfoScreen - Fixed swiftmodule directory resolution in build scripts - Added Ghostgram Settings tab in main Settings menu with all 5 features - Cleared sensitive credentials from config.json (template-only now) - Excluded bazel-cache from version control
This commit is contained in:
+191
-41
@@ -292,16 +292,18 @@ public class GlassBackgroundView: UIView {
|
||||
public let isDark: Bool
|
||||
public let tintColor: TintColor
|
||||
public let isInteractive: Bool
|
||||
public let isVisible: Bool
|
||||
|
||||
init(shape: Shape, isDark: Bool, tintColor: TintColor, isInteractive: Bool) {
|
||||
init(shape: Shape, isDark: Bool, tintColor: TintColor, isInteractive: Bool, isVisible: Bool) {
|
||||
self.shape = shape
|
||||
self.isDark = isDark
|
||||
self.tintColor = tintColor
|
||||
self.isInteractive = isInteractive
|
||||
self.isVisible = isVisible
|
||||
}
|
||||
}
|
||||
|
||||
private let backgroundNode: NavigationBackgroundNode?
|
||||
private let legacyView: LegacyGlassView?
|
||||
|
||||
private let nativeView: UIVisualEffectView?
|
||||
private let nativeViewClippingContext: ClippingShapeContext?
|
||||
@@ -330,7 +332,7 @@ public class GlassBackgroundView: UIView {
|
||||
|
||||
public override init(frame: CGRect) {
|
||||
if #available(iOS 26.0, *), !GlassBackgroundView.useCustomGlassImpl {
|
||||
self.backgroundNode = nil
|
||||
self.legacyView = nil
|
||||
|
||||
let glassEffect = UIGlassEffect(style: .regular)
|
||||
glassEffect.isInteractive = false
|
||||
@@ -346,8 +348,7 @@ public class GlassBackgroundView: UIView {
|
||||
self.foregroundView = nil
|
||||
self.shadowView = nil
|
||||
} else {
|
||||
let backgroundNode = NavigationBackgroundNode(color: .black, enableBlur: true, customBlurRadius: 8.0)
|
||||
self.backgroundNode = backgroundNode
|
||||
self.legacyView = LegacyGlassView(frame: CGRect())
|
||||
self.nativeView = nil
|
||||
self.nativeViewClippingContext = nil
|
||||
self.nativeParamsView = nil
|
||||
@@ -375,8 +376,8 @@ public class GlassBackgroundView: UIView {
|
||||
if let nativeParamsView = self.nativeParamsView {
|
||||
self.addSubview(nativeParamsView)
|
||||
}
|
||||
if let backgroundNode = self.backgroundNode {
|
||||
self.addSubview(backgroundNode.view)
|
||||
if let legacyView = self.legacyView {
|
||||
self.addSubview(legacyView)
|
||||
}
|
||||
if let foregroundView = self.foregroundView {
|
||||
self.addSubview(foregroundView)
|
||||
@@ -390,6 +391,15 @@ public class GlassBackgroundView: UIView {
|
||||
}
|
||||
|
||||
override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||
if !self.isUserInteractionEnabled {
|
||||
return nil
|
||||
}
|
||||
if self.isHidden {
|
||||
return nil
|
||||
}
|
||||
if self.alpha == 0.0 {
|
||||
return nil
|
||||
}
|
||||
if let nativeView = self.nativeView {
|
||||
if let result = nativeView.hitTest(self.convert(point, to: nativeView), with: event) {
|
||||
return result
|
||||
@@ -402,11 +412,11 @@ public class GlassBackgroundView: UIView {
|
||||
return nil
|
||||
}
|
||||
|
||||
public func update(size: CGSize, cornerRadius: CGFloat, isDark: Bool, tintColor: TintColor, isInteractive: Bool = false, transition: ComponentTransition) {
|
||||
self.update(size: size, shape: .roundedRect(cornerRadius: cornerRadius), isDark: isDark, tintColor: tintColor, isInteractive: isInteractive, transition: transition)
|
||||
public func update(size: CGSize, cornerRadius: CGFloat, isDark: Bool, tintColor: TintColor, isInteractive: Bool = false, isVisible: Bool = true, transition: ComponentTransition) {
|
||||
self.update(size: size, shape: .roundedRect(cornerRadius: cornerRadius), isDark: isDark, tintColor: tintColor, isInteractive: isInteractive, isVisible: isVisible, transition: transition)
|
||||
}
|
||||
|
||||
public func update(size: CGSize, shape: Shape, isDark: Bool, tintColor: TintColor, isInteractive: Bool = false, transition: ComponentTransition) {
|
||||
public func update(size: CGSize, shape: Shape, isDark: Bool, tintColor: TintColor, isInteractive: Bool = false, isVisible: Bool = true, transition: ComponentTransition) {
|
||||
if let nativeView = self.nativeView, let nativeViewClippingContext = self.nativeViewClippingContext, (nativeView.bounds.size != size || nativeViewClippingContext.shape != shape) {
|
||||
|
||||
nativeViewClippingContext.update(shape: shape, size: size, transition: transition)
|
||||
@@ -416,15 +426,15 @@ public class GlassBackgroundView: UIView {
|
||||
let nativeFrame = CGRect(origin: CGPoint(), size: size)
|
||||
transition.setFrame(view: nativeView, frame: nativeFrame)
|
||||
}
|
||||
nativeView.overrideUserInterfaceStyle = isDark ? .dark : .light
|
||||
}
|
||||
if let backgroundNode = self.backgroundNode {
|
||||
backgroundNode.updateColor(color: .clear, forceKeepBlur: tintColor.color.alpha != 1.0, transition: transition.containedViewLayoutTransition)
|
||||
|
||||
if let legacyView = self.legacyView {
|
||||
switch shape {
|
||||
case let .roundedRect(cornerRadius):
|
||||
backgroundNode.update(size: size, cornerRadius: cornerRadius, transition: transition.containedViewLayoutTransition)
|
||||
legacyView.update(size: size, cornerRadius: cornerRadius, transition: transition)
|
||||
}
|
||||
transition.setFrame(view: backgroundNode.view, frame: CGRect(origin: CGPoint(), size: size))
|
||||
transition.setFrame(view: legacyView, frame: CGRect(origin: CGPoint(), size: size))
|
||||
transition.setAlpha(view: legacyView, alpha: isVisible ? 1.0 : 0.0)
|
||||
}
|
||||
|
||||
let shadowInset: CGFloat = 32.0
|
||||
@@ -468,7 +478,7 @@ public class GlassBackgroundView: UIView {
|
||||
innerBackgroundView.removeFromSuperview()
|
||||
}
|
||||
|
||||
let params = Params(shape: shape, isDark: isDark, tintColor: tintColor, isInteractive: isInteractive)
|
||||
let params = Params(shape: shape, isDark: isDark, tintColor: tintColor, isInteractive: isInteractive, isVisible: isVisible)
|
||||
if self.params != params {
|
||||
self.params = params
|
||||
|
||||
@@ -491,36 +501,73 @@ public class GlassBackgroundView: UIView {
|
||||
context.setBlendMode(.copy)
|
||||
context.fillEllipse(in: CGRect(origin: CGPoint(x: shadowInset + shadowInnerInset, y: shadowInset + shadowInnerInset), size: CGSize(width: size.width - shadowInset * 2.0 - shadowInnerInset * 2.0, height: size.height - shadowInset * 2.0 - shadowInnerInset * 2.0)))
|
||||
})?.stretchableImage(withLeftCapWidth: Int(shadowInset + outerCornerRadius), topCapHeight: Int(shadowInset + outerCornerRadius))
|
||||
transition.setAlpha(view: shadowView, alpha: isVisible ? 1.0 : 0.0)
|
||||
}
|
||||
|
||||
if let foregroundView = self.foregroundView {
|
||||
foregroundView.image = GlassBackgroundView.generateLegacyGlassImage(size: CGSize(width: outerCornerRadius * 2.0, height: outerCornerRadius * 2.0), inset: shadowInset, isDark: isDark, fillColor: tintColor.color)
|
||||
let fillColor: UIColor
|
||||
switch tintColor.kind {
|
||||
case .panel:
|
||||
if isDark {
|
||||
fillColor = UIColor(white: 1.0, alpha: 1.0).mixedWith(.black, alpha: 1.0 - 0.11).withAlphaComponent(0.85)
|
||||
} else {
|
||||
fillColor = UIColor(white: 1.0, alpha: 0.7)
|
||||
}
|
||||
case .custom:
|
||||
fillColor = tintColor.color
|
||||
}
|
||||
foregroundView.image = GlassBackgroundView.generateLegacyGlassImage(size: CGSize(width: outerCornerRadius * 2.0, height: outerCornerRadius * 2.0), inset: shadowInset, isDark: isDark, fillColor: fillColor)
|
||||
transition.setAlpha(view: foregroundView, alpha: isVisible ? 1.0 : 0.0)
|
||||
} else {
|
||||
if let nativeParamsView = self.nativeParamsView, let nativeView = self.nativeView {
|
||||
if #available(iOS 26.0, *) {
|
||||
let glassEffect = UIGlassEffect(style: .regular)
|
||||
switch tintColor.kind {
|
||||
case .panel:
|
||||
glassEffect.tintColor = UIColor(white: isDark ? 0.0 : 1.0, alpha: 0.1)
|
||||
case .custom:
|
||||
glassEffect.tintColor = tintColor.color
|
||||
}
|
||||
glassEffect.isInteractive = params.isInteractive
|
||||
var glassEffect: UIGlassEffect?
|
||||
|
||||
if transition.animation.isImmediate {
|
||||
nativeView.effect = glassEffect
|
||||
if isVisible {
|
||||
let glassEffectValue = UIGlassEffect(style: .regular)
|
||||
switch tintColor.kind {
|
||||
case .panel:
|
||||
if isDark {
|
||||
glassEffectValue.tintColor = UIColor(white: 1.0, alpha: 0.025)
|
||||
} else {
|
||||
glassEffectValue.tintColor = UIColor(white: 1.0, alpha: 0.1)
|
||||
}
|
||||
case .custom:
|
||||
glassEffectValue.tintColor = tintColor.color
|
||||
}
|
||||
glassEffectValue.isInteractive = params.isInteractive
|
||||
glassEffect = glassEffectValue
|
||||
}
|
||||
|
||||
if glassEffect == nil {
|
||||
if nativeView.effect is UIGlassEffect {
|
||||
if transition.animation.isImmediate {
|
||||
nativeView.effect = nil
|
||||
} else {
|
||||
UIView.animate {
|
||||
nativeView.effect = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
UIView.animate(withDuration: 0.2, animations: {
|
||||
if transition.animation.isImmediate {
|
||||
nativeView.effect = glassEffect
|
||||
})
|
||||
} else {
|
||||
if let glassEffect, let currentEffect = nativeView.effect as? UIGlassEffect, currentEffect.tintColor == glassEffect.tintColor, currentEffect.isInteractive == glassEffect.isInteractive {
|
||||
} else {
|
||||
UIView.animate {
|
||||
nativeView.effect = glassEffect
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if isDark {
|
||||
nativeParamsView.lumaMin = 0.0
|
||||
nativeParamsView.lumaMax = 0.15
|
||||
} else {
|
||||
nativeParamsView.lumaMin = 0.6
|
||||
nativeParamsView.lumaMax = 0.61
|
||||
nativeParamsView.lumaMin = 0.8
|
||||
nativeParamsView.lumaMax = 0.801
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -537,6 +584,10 @@ public class GlassBackgroundView: UIView {
|
||||
}
|
||||
transition.setFrame(view: self.contentContainer, frame: CGRect(origin: CGPoint(), size: size))
|
||||
}
|
||||
|
||||
override public func willMove(toWindow newWindow: UIWindow?) {
|
||||
super.willMove(toWindow: newWindow)
|
||||
}
|
||||
}
|
||||
|
||||
public final class GlassBackgroundContainerView: UIView {
|
||||
@@ -595,9 +646,25 @@ public final class GlassBackgroundContainerView: UIView {
|
||||
}
|
||||
|
||||
override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||
if self.alpha.isZero {
|
||||
return nil
|
||||
}
|
||||
if self.isHidden {
|
||||
return nil
|
||||
}
|
||||
if !self.isUserInteractionEnabled {
|
||||
return nil
|
||||
}
|
||||
for view in self.contentView.subviews.reversed() {
|
||||
if let result = view.hitTest(self.convert(point, to: view), with: event), result.isUserInteractionEnabled {
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
guard let result = self.contentView.hitTest(point, with: event) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -609,8 +676,8 @@ public final class GlassBackgroundContainerView: UIView {
|
||||
nativeParamsView.lumaMin = 0.0
|
||||
nativeParamsView.lumaMax = 0.15
|
||||
} else {
|
||||
nativeParamsView.lumaMin = 0.6
|
||||
nativeParamsView.lumaMax = 0.61
|
||||
nativeParamsView.lumaMin = 0.8
|
||||
nativeParamsView.lumaMax = 0.801
|
||||
}
|
||||
|
||||
transition.setFrame(view: nativeView, frame: CGRect(origin: CGPoint(), size: size))
|
||||
@@ -618,6 +685,10 @@ public final class GlassBackgroundContainerView: UIView {
|
||||
transition.setFrame(view: legacyView, frame: CGRect(origin: CGPoint(), size: size))
|
||||
}
|
||||
}
|
||||
|
||||
override public func willMove(toWindow newWindow: UIWindow?) {
|
||||
super.willMove(toWindow: newWindow)
|
||||
}
|
||||
}
|
||||
|
||||
private extension CGContext {
|
||||
@@ -736,8 +807,8 @@ public extension GlassBackgroundView {
|
||||
}
|
||||
}
|
||||
|
||||
addShadow(context, true, CGPoint(), 10.0, 0.0, UIColor(white: 0.0, alpha: 0.06), .normal)
|
||||
addShadow(context, true, CGPoint(), 20.0, 0.0, UIColor(white: 0.0, alpha: 0.06), .normal)
|
||||
addShadow(context, true, CGPoint(), 30.0, 0.0, UIColor(white: 0.0, alpha: 0.045), .normal)
|
||||
addShadow(context, true, CGPoint(), 20.0, 0.0, UIColor(white: 0.0, alpha: 0.01), .normal)
|
||||
|
||||
var a: CGFloat = 0.0
|
||||
var b: CGFloat = 0.0
|
||||
@@ -745,7 +816,7 @@ public extension GlassBackgroundView {
|
||||
fillColor.getHue(nil, saturation: &s, brightness: &b, alpha: &a)
|
||||
|
||||
let innerImage: UIImage
|
||||
if size == CGSize(width: 40.0 + inset * 2.0, height: 40.0 + inset * 2.0), b >= 0.2 {
|
||||
/*if size == CGSize(width: 40.0 + inset * 2.0, height: 40.0 + inset * 2.0), b >= 0.2 {
|
||||
innerImage = UIGraphicsImageRenderer(size: size).image { ctx in
|
||||
let context = ctx.cgContext
|
||||
|
||||
@@ -798,10 +869,78 @@ public extension GlassBackgroundView {
|
||||
addShadow(context, false, CGPoint(x: 0.64, y: 0.64), 1.2, 0.0, UIColor(white: 1.0, alpha: edgeAlpha), .normal)
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
context.addEllipse(in: CGRect(origin: CGPoint(x: inset, y: inset), size: innerSize))
|
||||
context.clip()
|
||||
innerImage = UIGraphicsImageRenderer(size: size).image { ctx in
|
||||
let context = ctx.cgContext
|
||||
|
||||
context.setFillColor(fillColor.cgColor)
|
||||
var ellipseRect = CGRect(origin: CGPoint(), size: size).insetBy(dx: inset, dy: inset)
|
||||
context.fillEllipse(in: ellipseRect)
|
||||
|
||||
let lineWidth: CGFloat = isDark ? 0.8 : 0.8
|
||||
let strokeColor: UIColor
|
||||
let blendMode: CGBlendMode
|
||||
let baseAlpha: CGFloat = isDark ? 0.3 : 0.6
|
||||
|
||||
if s == 0.0 && abs(a - 0.7) < 0.1 && !isDark {
|
||||
blendMode = .normal
|
||||
strokeColor = UIColor(white: 1.0, alpha: baseAlpha)
|
||||
} else if s <= 0.3 && !isDark {
|
||||
blendMode = .normal
|
||||
strokeColor = UIColor(white: 1.0, alpha: 0.7 * baseAlpha)
|
||||
} else if b >= 0.2 {
|
||||
let maxAlpha: CGFloat = isDark ? 0.7 : 0.8
|
||||
blendMode = .overlay
|
||||
strokeColor = UIColor(white: 1.0, alpha: max(0.5, min(1.0, maxAlpha * s)) * baseAlpha)
|
||||
} else {
|
||||
blendMode = .normal
|
||||
strokeColor = UIColor(white: 1.0, alpha: 0.5 * baseAlpha)
|
||||
}
|
||||
|
||||
context.setStrokeColor(strokeColor.cgColor)
|
||||
ellipseRect = CGRect(origin: CGPoint(), size: size).insetBy(dx: inset, dy: inset)
|
||||
context.addEllipse(in: ellipseRect)
|
||||
context.clip()
|
||||
|
||||
ellipseRect = CGRect(origin: CGPoint(), size: size).insetBy(dx: inset, dy: inset).insetBy(dx: lineWidth * 0.5, dy: lineWidth * 0.5)
|
||||
|
||||
context.setBlendMode(blendMode)
|
||||
|
||||
let radius = ellipseRect.height * 0.5
|
||||
let smallerRadius = radius - lineWidth * 1.33
|
||||
context.move(to: CGPoint(x: ellipseRect.minX, y: ellipseRect.minY + radius))
|
||||
// Top-left corner (regular radius)
|
||||
context.addArc(tangent1End: CGPoint(x: ellipseRect.minX, y: ellipseRect.minY), tangent2End: CGPoint(x: ellipseRect.minX + radius, y: ellipseRect.minY), radius: radius)
|
||||
context.addLine(to: CGPoint(x: ellipseRect.maxX - smallerRadius, y: ellipseRect.minY))
|
||||
// Top-right corner (smaller radius)
|
||||
context.addArc(tangent1End: CGPoint(x: ellipseRect.maxX, y: ellipseRect.minY), tangent2End: CGPoint(x: ellipseRect.maxX, y: ellipseRect.minY + smallerRadius), radius: smallerRadius)
|
||||
context.addLine(to: CGPoint(x: ellipseRect.maxX, y: ellipseRect.maxY - radius))
|
||||
// Bottom-right corner (regular radius)
|
||||
context.addArc(tangent1End: CGPoint(x: ellipseRect.maxX, y: ellipseRect.maxY), tangent2End: CGPoint(x: ellipseRect.maxX - radius, y: ellipseRect.maxY), radius: radius)
|
||||
context.addLine(to: CGPoint(x: ellipseRect.minX + smallerRadius, y: ellipseRect.maxY))
|
||||
// Bottom-left corner (smaller radius)
|
||||
context.addArc(tangent1End: CGPoint(x: ellipseRect.minX, y: ellipseRect.maxY), tangent2End: CGPoint(x: ellipseRect.minX, y: ellipseRect.maxY - smallerRadius), radius: smallerRadius)
|
||||
context.closePath()
|
||||
context.strokePath()
|
||||
|
||||
context.resetClip()
|
||||
context.setBlendMode(.normal)
|
||||
|
||||
//let image = makeInnerShadowPillImageExact(size: CGSize(width: size.width - inset * 2.0, height: size.height - inset * 2.0), scale: UIScreenScale, glossColor: UIColor(white: 1.0, alpha: 1.0), borderWidth: 1.33)
|
||||
/*let image = generateCircleImage(diameter: size.width - inset * 2.0, lineWidth: 0.5, color: UIColor(white: 1.0, alpha: 1.0))!
|
||||
|
||||
if s == 0.0 && abs(a - 0.7) < 0.1 && !isDark {
|
||||
image.draw(in: CGRect(origin: CGPoint(), size: size).insetBy(dx: inset, dy: inset), blendMode: .normal, alpha: 1.0)
|
||||
} else if s <= 0.3 && !isDark {
|
||||
image.draw(in: CGRect(origin: CGPoint(), size: size).insetBy(dx: inset, dy: inset), blendMode: .normal, alpha: 0.7)
|
||||
} else if b >= 0.2 {
|
||||
let maxAlpha: CGFloat = isDark ? 0.7 : 0.8
|
||||
image.draw(in: CGRect(origin: CGPoint(), size: size).insetBy(dx: inset, dy: inset), blendMode: .overlay, alpha: max(0.5, min(1.0, maxAlpha * s)))
|
||||
} else {
|
||||
image.draw(in: CGRect(origin: CGPoint(), size: size).insetBy(dx: inset, dy: inset), blendMode: .normal, alpha: 0.5)
|
||||
}*/
|
||||
}
|
||||
innerImage.draw(in: CGRect(origin: CGPoint(), size: size))
|
||||
}.stretchableImage(withLeftCapWidth: Int(size.width * 0.5), topCapHeight: Int(size.height * 0.5))
|
||||
}
|
||||
@@ -892,12 +1031,20 @@ public final class GlassBackgroundComponent: Component {
|
||||
private let cornerRadius: CGFloat
|
||||
private let isDark: Bool
|
||||
private let tintColor: GlassBackgroundView.TintColor
|
||||
private let isInteractive: Bool
|
||||
|
||||
public init(size: CGSize, cornerRadius: CGFloat, isDark: Bool, tintColor: GlassBackgroundView.TintColor) {
|
||||
public init(
|
||||
size: CGSize,
|
||||
cornerRadius: CGFloat,
|
||||
isDark: Bool,
|
||||
tintColor: GlassBackgroundView.TintColor,
|
||||
isInteractive: Bool = false
|
||||
) {
|
||||
self.size = size
|
||||
self.cornerRadius = cornerRadius
|
||||
self.isDark = isDark
|
||||
self.tintColor = tintColor
|
||||
self.isInteractive = isInteractive
|
||||
}
|
||||
|
||||
public static func == (lhs: GlassBackgroundComponent, rhs: GlassBackgroundComponent) -> Bool {
|
||||
@@ -913,12 +1060,15 @@ public final class GlassBackgroundComponent: Component {
|
||||
if lhs.tintColor != rhs.tintColor {
|
||||
return false
|
||||
}
|
||||
if lhs.isInteractive != rhs.isInteractive {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
public final class View: GlassBackgroundView {
|
||||
func update(component: GlassBackgroundComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
|
||||
self.update(size: component.size, cornerRadius: component.cornerRadius, isDark: component.isDark, tintColor: component.tintColor, transition: transition)
|
||||
self.update(size: component.size, cornerRadius: component.cornerRadius, isDark: component.isDark, tintColor: component.tintColor, isInteractive: component.isInteractive, transition: transition)
|
||||
|
||||
return component.size
|
||||
}
|
||||
|
||||
+178
@@ -0,0 +1,178 @@
|
||||
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")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user