2025-09-23 21:04:01 +08:00

818 lines
33 KiB
Swift

import Foundation
import UIKit
import Display
import ComponentFlow
import ComponentDisplayAdapters
private final class ContentContainer: UIView {
private let maskContentView: UIView
init(maskContentView: UIView) {
self.maskContentView = maskContentView
super.init(frame: CGRect())
}
required public init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
guard let result = super.hitTest(point, with: event) else {
return nil
}
if result === self {
return nil
}
return result
}
override func didAddSubview(_ subview: UIView) {
super.didAddSubview(subview)
if let subview = subview as? GlassBackgroundView.ContentView {
self.maskContentView.addSubview(subview.tintMask)
}
}
override func willRemoveSubview(_ subview: UIView) {
super.willRemoveSubview(subview)
if let subview = subview as? GlassBackgroundView.ContentView {
subview.tintMask.removeFromSuperview()
}
}
}
public class GlassBackgroundView: UIView {
public protocol ContentView: UIView {
var tintMask: UIView { get }
}
open class ContentLayer: SimpleLayer {
public var targetLayer: CALayer?
override init() {
super.init()
}
override init(layer: Any) {
super.init(layer: layer)
}
required public init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override public var position: CGPoint {
get {
return super.position
} set(value) {
if let targetLayer = self.targetLayer {
targetLayer.position = value
}
super.position = value
}
}
override public var bounds: CGRect {
get {
return super.bounds
} set(value) {
if let targetLayer = self.targetLayer {
targetLayer.bounds = value
}
super.bounds = value
}
}
override public var anchorPoint: CGPoint {
get {
return super.anchorPoint
} set(value) {
if let targetLayer = self.targetLayer {
targetLayer.anchorPoint = value
}
super.anchorPoint = value
}
}
override public var anchorPointZ: CGFloat {
get {
return super.anchorPointZ
} set(value) {
if let targetLayer = self.targetLayer {
targetLayer.anchorPointZ = value
}
super.anchorPointZ = value
}
}
override public var opacity: Float {
get {
return super.opacity
} set(value) {
if let targetLayer = self.targetLayer {
targetLayer.opacity = value
}
super.opacity = value
}
}
override public var sublayerTransform: CATransform3D {
get {
return super.sublayerTransform
} set(value) {
if let targetLayer = self.targetLayer {
targetLayer.sublayerTransform = value
}
super.sublayerTransform = value
}
}
override public var transform: CATransform3D {
get {
return super.transform
} set(value) {
if let targetLayer = self.targetLayer {
targetLayer.transform = value
}
super.transform = value
}
}
override public func add(_ animation: CAAnimation, forKey key: String?) {
if let targetLayer = self.targetLayer {
targetLayer.add(animation, forKey: key)
}
super.add(animation, forKey: key)
}
override public func removeAllAnimations() {
if let targetLayer = self.targetLayer {
targetLayer.removeAllAnimations()
}
super.removeAllAnimations()
}
override public func removeAnimation(forKey: String) {
if let targetLayer = self.targetLayer {
targetLayer.removeAnimation(forKey: forKey)
}
super.removeAnimation(forKey: forKey)
}
}
public final class ContentColorView: UIView, ContentView {
override public static var layerClass: AnyClass {
return ContentLayer.self
}
public let tintMask: UIView
override public init(frame: CGRect) {
self.tintMask = UIView()
super.init(frame: CGRect())
self.tintMask.tintColor = .black
}
required public init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
public final class ContentImageView: UIImageView, ContentView {
override public static var layerClass: AnyClass {
return ContentLayer.self
}
private let tintImageView: UIImageView
public var tintMask: UIView {
return self.tintImageView
}
override public var image: UIImage? {
didSet {
self.tintImageView.image = self.image
}
}
override public var tintColor: UIColor? {
didSet {
if self.tintColor != oldValue {
self.setMonochromaticEffect(tintColor: self.tintColor)
}
}
}
override public init(frame: CGRect) {
self.tintImageView = UIImageView()
super.init(frame: CGRect())
self.tintImageView.tintColor = .black
}
override public init(image: UIImage?) {
self.tintImageView = UIImageView()
super.init(image: image)
self.tintImageView.image = image
self.tintImageView.tintColor = .black
}
override public init(image: UIImage?, highlightedImage: UIImage?) {
self.tintImageView = UIImageView()
super.init(image: image, highlightedImage: highlightedImage)
self.tintImageView.image = image
self.tintImageView.tintColor = .black
}
required public init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
public struct TintColor: Equatable {
public enum Kind {
case panel
case custom
}
public let kind: Kind
public let color: UIColor
public init(kind: Kind, color: UIColor) {
self.kind = kind
self.color = color
}
}
private struct Params: Equatable {
let cornerRadius: CGFloat
let isDark: Bool
let tintColor: TintColor
let isInteractive: Bool
init(cornerRadius: CGFloat, isDark: Bool, tintColor: TintColor, isInteractive: Bool) {
self.cornerRadius = cornerRadius
self.isDark = isDark
self.tintColor = tintColor
self.isInteractive = isInteractive
}
}
private let backgroundNode: NavigationBackgroundNode?
private let nativeView: UIVisualEffectView?
private let nativeContainerView: UIVisualEffectView?
private let foregroundView: UIImageView?
private let shadowView: UIImageView?
private let maskContainerView: UIView
public let maskContentView: UIView
private let contentContainer: ContentContainer
public var contentView: UIView {
if let nativeView = self.nativeView {
return nativeView.contentView
} else {
return self.contentContainer
}
}
private var params: Params?
public override init(frame: CGRect) {
if #available(iOS 26.0, *) {
self.backgroundNode = nil
let glassEffect = UIGlassEffect(style: .regular)
glassEffect.isInteractive = false
let nativeView = UIVisualEffectView(effect: glassEffect)
self.nativeView = nativeView
let glassContainerEffect = UIGlassContainerEffect()
let nativeContainerView = UIVisualEffectView(effect: glassContainerEffect)
self.nativeContainerView = nativeContainerView
nativeContainerView.contentView.addSubview(nativeView)
self.foregroundView = nil
self.shadowView = nil
} else {
self.backgroundNode = NavigationBackgroundNode(color: .black, enableBlur: true, customBlurRadius: 5.0)
self.nativeView = nil
self.nativeContainerView = nil
self.foregroundView = UIImageView()
self.shadowView = UIImageView()
}
self.maskContainerView = UIView()
self.maskContainerView.backgroundColor = .white
if let filter = CALayer.luminanceToAlpha() {
self.maskContainerView.layer.filters = [filter]
}
self.maskContentView = UIView()
self.maskContainerView.addSubview(self.maskContentView)
self.contentContainer = ContentContainer(maskContentView: self.maskContentView)
super.init(frame: frame)
if let shadowView = self.shadowView {
self.addSubview(shadowView)
}
if let nativeContainerView = self.nativeContainerView {
self.addSubview(nativeContainerView)
}
if let backgroundNode = self.backgroundNode {
self.addSubview(backgroundNode.view)
}
if let foregroundView = self.foregroundView {
self.addSubview(foregroundView)
foregroundView.mask = self.maskContainerView
}
self.addSubview(self.contentContainer)
}
required public init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
/*if let nativeContainerView = self.nativeContainerView {
if let result = nativeContainerView.hitTest(self.convert(point, to: nativeContainerView), with: event) {
return result
}
}*/
return nil
}
public func update(size: CGSize, cornerRadius: CGFloat, isDark: Bool, tintColor: TintColor, isInteractive: Bool = false, transition: ComponentTransition) {
if let nativeContainerView = self.nativeContainerView, let nativeView = self.nativeView {
let previousFrame = nativeView.frame
if transition.animation.isImmediate {
nativeView.layer.cornerRadius = cornerRadius
nativeView.frame = CGRect(origin: CGPoint(), size: size)
} else {
transition.containedViewLayoutTransition.animateView {
nativeView.layer.cornerRadius = cornerRadius
nativeView.frame = CGRect(origin: CGPoint(), size: size)
}
nativeView.layer.animateFrame(from: previousFrame, to: CGRect(origin: CGPoint(), size: size), duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring)
}
transition.setFrame(view: nativeContainerView, frame: CGRect(origin: CGPoint(), size: size))
}
if let backgroundNode = self.backgroundNode {
backgroundNode.updateColor(color: .clear, forceKeepBlur: tintColor.color.alpha != 1.0, transition: transition.containedViewLayoutTransition)
backgroundNode.update(size: size, cornerRadius: cornerRadius, transition: transition.containedViewLayoutTransition)
transition.setFrame(view: backgroundNode.view, frame: CGRect(origin: CGPoint(), size: size))
}
let shadowInset: CGFloat = 32.0
let params = Params(cornerRadius: cornerRadius, isDark: isDark, tintColor: tintColor, isInteractive: isInteractive)
if self.params != params {
self.params = params
if let shadowView = self.shadowView {
let shadowInnerInset: CGFloat = 0.5
shadowView.image = generateImage(CGSize(width: shadowInset * 2.0 + cornerRadius * 2.0, height: shadowInset * 2.0 + cornerRadius * 2.0), rotatedContext: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
context.setFillColor(UIColor.black.cgColor)
context.setShadow(offset: CGSize(width: 0.0, height: 1.0), blur: 40.0, color: UIColor(white: 0.0, alpha: 0.09).cgColor)
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)))
context.setFillColor(UIColor.clear.cgColor)
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 + cornerRadius), topCapHeight: Int(shadowInset + cornerRadius))
}
if let foregroundView = self.foregroundView {
foregroundView.image = GlassBackgroundView.generateLegacyGlassImage(size: CGSize(width: cornerRadius * 2.0, height: cornerRadius * 2.0), inset: shadowInset, isDark: isDark, fillColor: tintColor.color)
} else {
if let nativeContainerView = self.nativeContainerView, let nativeView {
if #available(iOS 26.0, *) {
let glassEffect = UIGlassEffect(style: .regular)
switch tintColor.kind {
case .panel:
glassEffect.tintColor = nil
case .custom:
glassEffect.tintColor = tintColor.color
}
glassEffect.isInteractive = params.isInteractive
nativeView.effect = glassEffect
let _ = nativeContainerView
//nativeContainerView.overrideUserInterfaceStyle = .light// isDark ? .dark : .light
self.overrideUserInterfaceStyle = isDark ? .dark : .light
}
}
}
}
transition.setFrame(view: self.maskContainerView, frame: CGRect(origin: CGPoint(), size: CGSize(width: size.width + shadowInset * 2.0, height: size.height + shadowInset * 2.0)))
transition.setFrame(view: self.maskContentView, frame: CGRect(origin: CGPoint(x: shadowInset, y: shadowInset), size: size))
if let foregroundView = self.foregroundView {
transition.setFrame(view: foregroundView, frame: CGRect(origin: CGPoint(), size: size).insetBy(dx: -shadowInset, dy: -shadowInset))
}
if let shadowView = self.shadowView {
transition.setFrame(view: shadowView, frame: CGRect(origin: CGPoint(), size: size).insetBy(dx: -shadowInset, dy: -shadowInset))
}
transition.setFrame(view: self.contentContainer, frame: CGRect(origin: CGPoint(), size: size))
}
}
public final class GlassBackgroundContainerView: UIView {
private final class ContentView: UIView {
}
private let contentViewImpl: ContentView
public var contentView: UIView {
return self.contentViewImpl
}
public override init(frame: CGRect) {
self.contentViewImpl = ContentView()
super.init(frame: frame)
}
required public init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
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")
variableBlur.setValue(UIScreenScale, forKey: "scale")
let backdropLayer = self.subviews.first?.layer
backdropLayer?.filters = [variableBlur]
}
}
public extension GlassBackgroundView {
static func generateLegacyGlassImage(size: CGSize, inset: CGFloat, isDark: Bool, fillColor: UIColor) -> UIImage {
var size = size
if size == .zero {
size = CGSize(width: 1.0, height: 1.0)
}
let innerSize = size
size.width += inset * 2.0
size.height += inset * 2.0
return generateImage(size, rotatedContext: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
func pathApplyingSpread(_ path: CGPath, spread: CGFloat) -> CGPath {
guard spread != 0 else { return path }
let result = CGMutablePath()
result.addPath(path)
// Copy a stroked outline centered on the original path boundary.
// Filling it plus the original path approximates an outward "spread".
let outline = path.copy(
strokingWithWidth: abs(spread) * 2,
lineCap: .butt,
lineJoin: .miter,
miterLimit: 10,
transform: .identity
)
result.addPath(outline)
// For negative spread (tighten), use even-odd to carve inside:
if spread < 0 {
let carve = CGMutablePath()
carve.addPath(path)
carve.addPath(outline)
// even-odd: outline - original outer ring; union with original earlier keeps overall stable
// For "tightening" effect we rely on clipping in inner shadow branch below.
}
return result
}
let addShadow: (Bool, CGPoint, CGFloat, CGFloat, UIColor, Bool) -> Void = { isOuter, position, blur, spread, shadowColor, isMultiply in
var blur = blur
blur += abs(spread)
if isOuter {
context.beginTransparencyLayer(auxiliaryInfo: nil)
context.saveGState()
defer {
context.restoreGState()
context.endTransparencyLayer()
}
let spreadRect = CGRect(origin: CGPoint(x: inset, y: inset), size: innerSize).insetBy(dx: 0.25, dy: 0.25)
let spreadPath = UIBezierPath(
roundedRect: spreadRect,
cornerRadius: min(spreadRect.width, spreadRect.height) * 0.5
).cgPath
context.setShadow(offset: CGSize(width: position.x, height: position.y), blur: blur, color: shadowColor.cgColor)
context.setFillColor(UIColor.black.withAlphaComponent(1.0).cgColor)
context.addPath(spreadPath)
context.fillPath()
let cleanRect = CGRect(origin: CGPoint(x: inset, y: inset), size: innerSize)
let cleanPath = UIBezierPath(
roundedRect: cleanRect,
cornerRadius: min(cleanRect.width, cleanRect.height) * 0.5
).cgPath
context.setBlendMode(.copy)
context.setFillColor(UIColor.clear.cgColor)
context.addPath(cleanPath)
context.fillPath()
context.setBlendMode(.normal)
} else {
if let image = generateImage(size, rotatedContext: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
let spreadRect = CGRect(origin: CGPoint(x: inset, y: inset), size: innerSize).insetBy(dx: -0.25, dy: -0.25)
let spreadPath = UIBezierPath(
roundedRect: spreadRect,
cornerRadius: min(spreadRect.width, spreadRect.height) * 0.5
).cgPath
context.setShadow(offset: CGSize(width: position.x, height: position.y), blur: blur, color: shadowColor.cgColor)
context.setFillColor(UIColor.black.withAlphaComponent(1.0).cgColor)
let enclosingRect = spreadRect.insetBy(dx: -10000.0, dy: -10000.0)
context.addPath(UIBezierPath(rect: enclosingRect).cgPath)
context.addPath(spreadPath)
context.fillPath(using: .evenOdd)
let cleanRect = CGRect(origin: CGPoint(x: inset, y: inset), size: innerSize)
let cleanPath = UIBezierPath(
roundedRect: cleanRect,
cornerRadius: min(cleanRect.width, cleanRect.height) * 0.5
).cgPath
context.setBlendMode(.copy)
context.setFillColor(UIColor.clear.cgColor)
context.addPath(UIBezierPath(rect: enclosingRect).cgPath)
context.addPath(cleanPath)
context.fillPath(using: .evenOdd)
context.setBlendMode(.normal)
}) {
UIGraphicsPushContext(context)
image.draw(in: CGRect(origin: .zero, size: size), blendMode: isMultiply ? .destinationOut : .normal, alpha: 1.0)
UIGraphicsPopContext()
}
}
}
if isDark {
addShadow(true, CGPoint(), 16.0, 0.0, UIColor(white: 0.0, alpha: 0.12), false)
addShadow(true, CGPoint(), 8.0, 0.0, UIColor(white: 0.0, alpha: 0.1), false)
context.setFillColor(fillColor.cgColor)
context.fillEllipse(in: CGRect(origin: CGPoint(), size: size).insetBy(dx: inset, dy: inset))
addShadow(false, CGPoint(x: 0.0, y: 0.0), 3.0, 0.0, UIColor(white: 1.0, alpha: 0.5), false)
addShadow(false, CGPoint(x: 3.0, y: -3.0), 2.0, 0.0, UIColor(white: 1.0, alpha: 0.25), false)
addShadow(false, CGPoint(x: -3.0, y: 3.0), 2.0, 0.0, UIColor(white: 1.0, alpha: 0.25), false)
} else {
addShadow(true, CGPoint(), 32.0, 0.0, UIColor(white: 0.0, alpha: 0.08), false)
addShadow(true, CGPoint(), 16.0, 0.0, UIColor(white: 0.0, alpha: 0.08), false)
context.setFillColor(fillColor.cgColor)
context.fillEllipse(in: CGRect(origin: CGPoint(), size: size).insetBy(dx: inset, dy: inset))
let highlightColor: UIColor
if fillColor.hsb.s > 0.5 {
highlightColor = fillColor.withMultiplied(hue: 1.0, saturation: 2.0, brightness: 1.0).adjustedPerceivedBrightness(2.0)
let shadowColor = fillColor.withMultiplied(hue: 1.0, saturation: 2.0, brightness: 1.0).adjustedPerceivedBrightness(0.5).withMultipliedAlpha(0.2)
addShadow(false, CGPoint(x: -2.0, y: 2.0), 0.5, 0.0, shadowColor, false)
} else {
highlightColor = UIColor(white: 1.0, alpha: 0.4)
addShadow(false, CGPoint(x: -2.0, y: 2.0), 0.5, 0.0, UIColor.black.withMultipliedAlpha(0.15), true)
addShadow(false, CGPoint(x: -2.0, y: 2.0), 0.6, 0.0, UIColor(white: 0.0, alpha: 0.1), false)
}
addShadow(false, CGPoint(x: 2.0, y: -2.0), 0.5, 0.0, highlightColor, false)
}
})!.stretchableImage(withLeftCapWidth: Int(size.width * 0.5), topCapHeight: Int(size.height * 0.5))
}
static func generateForegroundImage(size: CGSize, isDark: Bool, fillColor: UIColor) -> UIImage {
var size = size
if size == .zero {
size = CGSize(width: 1.0, height: 1.0)
}
return generateImage(size, rotatedContext: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
let maxColor = UIColor(white: 1.0, alpha: isDark ? 0.25 : 0.9)
let minColor = UIColor(white: 1.0, alpha: 0.0)
context.setFillColor(fillColor.cgColor)
context.fillEllipse(in: CGRect(origin: CGPoint(), size: size))
let lineWidth: CGFloat = isDark ? 0.66 : 0.66
context.saveGState()
let darkShadeColor = UIColor(white: isDark ? 1.0 : 0.0, alpha: 0.035)
let lightShadeColor = UIColor(white: isDark ? 0.0 : 1.0, alpha: 0.035)
let innerShadowBlur: CGFloat = 24.0
context.resetClip()
context.addEllipse(in: CGRect(origin: CGPoint(), size: size).insetBy(dx: lineWidth * 0.5, dy: lineWidth * 0.5))
context.clip()
context.addRect(CGRect(origin: CGPoint(), size: size).insetBy(dx: -100.0, dy: -100.0))
context.addEllipse(in: CGRect(origin: CGPoint(), size: size))
context.setFillColor(UIColor.black.cgColor)
context.setShadow(offset: CGSize(width: 10.0, height: -10.0), blur: innerShadowBlur, color: darkShadeColor.cgColor)
context.fillPath(using: .evenOdd)
context.resetClip()
context.addEllipse(in: CGRect(origin: CGPoint(), size: size).insetBy(dx: lineWidth * 0.5, dy: lineWidth * 0.5))
context.clip()
context.addRect(CGRect(origin: CGPoint(), size: size).insetBy(dx: -100.0, dy: -100.0))
context.addEllipse(in: CGRect(origin: CGPoint(), size: size))
context.setFillColor(UIColor.black.cgColor)
context.setShadow(offset: CGSize(width: -10.0, height: 10.0), blur: innerShadowBlur, color: lightShadeColor.cgColor)
context.fillPath(using: .evenOdd)
context.restoreGState()
context.setLineWidth(lineWidth)
context.addRect(CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width * 0.5, height: size.height)))
context.clip()
context.addEllipse(in: CGRect(origin: CGPoint(), size: size).insetBy(dx: lineWidth * 0.5, dy: lineWidth * 0.5))
context.replacePathWithStrokedPath()
context.clip()
do {
var locations: [CGFloat] = [0.0, 0.5, 0.5 + 0.2, 1.0 - 0.1, 1.0]
let colors: [CGColor] = [maxColor.cgColor, maxColor.cgColor, minColor.cgColor, minColor.cgColor, maxColor.cgColor]
let colorSpace = CGColorSpaceCreateDeviceRGB()
let gradient = CGGradient(colorsSpace: colorSpace, colors: colors as CFArray, locations: &locations)!
context.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: 0.0), end: CGPoint(x: 0.0, y: size.height), options: CGGradientDrawingOptions())
}
context.resetClip()
context.addRect(CGRect(origin: CGPoint(x: size.width - size.width * 0.5, y: 0.0), size: CGSize(width: size.width * 0.5, height: size.height)))
context.clip()
context.addEllipse(in: CGRect(origin: CGPoint(), size: size).insetBy(dx: lineWidth * 0.5, dy: lineWidth * 0.5))
context.replacePathWithStrokedPath()
context.clip()
do {
var locations: [CGFloat] = [0.0, 0.1, 0.5 - 0.2, 0.5, 1.0]
let colors: [CGColor] = [maxColor.cgColor, minColor.cgColor, minColor.cgColor, maxColor.cgColor, maxColor.cgColor]
let colorSpace = CGColorSpaceCreateDeviceRGB()
let gradient = CGGradient(colorsSpace: colorSpace, colors: colors as CFArray, locations: &locations)!
context.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: 0.0), end: CGPoint(x: 0.0, y: size.height), options: CGGradientDrawingOptions())
}
})!.stretchableImage(withLeftCapWidth: Int(size.width * 0.5), topCapHeight: Int(size.height * 0.5))
}
}
public final class GlassBackgroundComponent: Component {
private let size: CGSize
private let cornerRadius: CGFloat
private let isDark: Bool
private let tintColor: GlassBackgroundView.TintColor
public init(size: CGSize, cornerRadius: CGFloat, isDark: Bool, tintColor: GlassBackgroundView.TintColor) {
self.size = size
self.cornerRadius = cornerRadius
self.isDark = isDark
self.tintColor = tintColor
}
public static func == (lhs: GlassBackgroundComponent, rhs: GlassBackgroundComponent) -> Bool {
if lhs.size != rhs.size {
return false
}
if lhs.cornerRadius != rhs.cornerRadius {
return false
}
if lhs.isDark != rhs.isDark {
return false
}
if lhs.tintColor != rhs.tintColor {
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.frame = CGRect(origin: .zero, size: component.size)
return component.size
}
}
public func makeView() -> View {
return View()
}
public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<EnvironmentType>, transition: ComponentTransition) -> CGSize {
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
}
}