mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
147 lines
5.6 KiB
Swift
147 lines
5.6 KiB
Swift
import Foundation
|
|
import UIKit
|
|
import AsyncDisplayKit
|
|
import Display
|
|
|
|
private final class MultiScaleTextStateNode: ASDisplayNode {
|
|
let tintTextNode: ImmediateTextNode
|
|
let noTintTextNode: ImmediateTextNode
|
|
|
|
var currentLayout: MultiScaleTextLayout?
|
|
|
|
override init() {
|
|
self.tintTextNode = ImmediateTextNode()
|
|
self.tintTextNode.displaysAsynchronously = false
|
|
self.tintTextNode.renderContentTypes = TextNode.RenderContentTypes.all.subtracting(TextNode.RenderContentTypes.emoji)
|
|
|
|
self.noTintTextNode = ImmediateTextNode()
|
|
self.noTintTextNode.displaysAsynchronously = false
|
|
self.noTintTextNode.renderContentTypes = .emoji
|
|
|
|
super.init()
|
|
|
|
self.addSubnode(self.tintTextNode)
|
|
self.addSubnode(self.noTintTextNode)
|
|
}
|
|
}
|
|
|
|
public final class MultiScaleTextState {
|
|
public struct Attributes {
|
|
public var font: UIFont
|
|
public var color: UIColor
|
|
public var shadowColor: UIColor?
|
|
|
|
public init(font: UIFont, color: UIColor, shadowColor: UIColor? = nil) {
|
|
self.font = font
|
|
self.color = color
|
|
self.shadowColor = shadowColor
|
|
}
|
|
}
|
|
|
|
public let attributes: Attributes
|
|
public let constrainedSize: CGSize
|
|
|
|
public init(attributes: Attributes, constrainedSize: CGSize) {
|
|
self.attributes = attributes
|
|
self.constrainedSize = constrainedSize
|
|
}
|
|
}
|
|
|
|
public struct MultiScaleTextLayout {
|
|
public var size: CGSize
|
|
|
|
public init(size: CGSize) {
|
|
self.size = size
|
|
}
|
|
}
|
|
|
|
public final class MultiScaleTextNode: ASDisplayNode {
|
|
private let stateNodes: [AnyHashable: MultiScaleTextStateNode]
|
|
|
|
public init(stateKeys: [AnyHashable]) {
|
|
self.stateNodes = Dictionary(stateKeys.map { ($0, MultiScaleTextStateNode()) }, uniquingKeysWith: { lhs, _ in lhs })
|
|
|
|
super.init()
|
|
|
|
for (_, node) in self.stateNodes {
|
|
self.addSubnode(node)
|
|
}
|
|
}
|
|
|
|
public func stateNode(forKey key: AnyHashable) -> ASDisplayNode? {
|
|
return self.stateNodes[key]?.tintTextNode
|
|
}
|
|
|
|
public func updateTintColor(color: UIColor, transition: ContainedViewLayoutTransition) {
|
|
for (_, node) in self.stateNodes {
|
|
transition.updateTintColor(layer: node.tintTextNode.layer, color: color)
|
|
}
|
|
}
|
|
|
|
public func updateLayout(text: String, states: [AnyHashable: MultiScaleTextState], mainState: AnyHashable) -> [AnyHashable: MultiScaleTextLayout] {
|
|
assert(Set(states.keys) == Set(self.stateNodes.keys))
|
|
assert(states[mainState] != nil)
|
|
|
|
var result: [AnyHashable: MultiScaleTextLayout] = [:]
|
|
var mainLayout: MultiScaleTextLayout?
|
|
for (key, state) in states {
|
|
if let node = self.stateNodes[key] {
|
|
node.tintTextNode.attributedText = NSAttributedString(string: text, attributes: [
|
|
.font: state.attributes.font,
|
|
.foregroundColor: state.attributes.color
|
|
])
|
|
node.noTintTextNode.attributedText = NSAttributedString(string: text, attributes: [
|
|
.font: state.attributes.font,
|
|
.foregroundColor: state.attributes.color
|
|
])
|
|
if let shadowColor = state.attributes.shadowColor {
|
|
node.tintTextNode.textShadowColor = shadowColor
|
|
node.tintTextNode.textShadowBlur = 3.0
|
|
node.noTintTextNode.textShadowColor = shadowColor
|
|
node.noTintTextNode.textShadowBlur = 3.0
|
|
} else {
|
|
node.tintTextNode.shadowColor = nil
|
|
node.noTintTextNode.shadowColor = nil
|
|
}
|
|
node.tintTextNode.isAccessibilityElement = true
|
|
node.tintTextNode.accessibilityLabel = text
|
|
node.noTintTextNode.isAccessibilityElement = false
|
|
let nodeSize = node.tintTextNode.updateLayout(state.constrainedSize)
|
|
let _ = node.noTintTextNode.updateLayout(state.constrainedSize)
|
|
let nodeLayout = MultiScaleTextLayout(size: nodeSize)
|
|
if key == mainState {
|
|
mainLayout = nodeLayout
|
|
}
|
|
node.currentLayout = nodeLayout
|
|
result[key] = nodeLayout
|
|
}
|
|
}
|
|
if let mainLayout = mainLayout {
|
|
let mainBounds = CGRect(origin: CGPoint(x: -mainLayout.size.width / 2.0, y: -mainLayout.size.height / 2.0), size: mainLayout.size)
|
|
for (key, _) in states {
|
|
if let node = self.stateNodes[key], let nodeLayout = result[key] {
|
|
let textFrame = CGRect(origin: CGPoint(x: mainBounds.minX, y: mainBounds.minY + floor((mainBounds.height - nodeLayout.size.height) / 2.0)), size: nodeLayout.size)
|
|
node.tintTextNode.frame = textFrame
|
|
node.noTintTextNode.frame = textFrame
|
|
}
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
|
|
public func update(stateFractions: [AnyHashable: CGFloat], alpha: CGFloat = 1.0, transition: ContainedViewLayoutTransition) {
|
|
var fractionSum: CGFloat = 0.0
|
|
for (_, fraction) in stateFractions {
|
|
fractionSum += fraction
|
|
}
|
|
for (key, fraction) in stateFractions {
|
|
if let node = self.stateNodes[key], let _ = node.currentLayout {
|
|
if !transition.isAnimated {
|
|
node.layer.removeAllAnimations()
|
|
}
|
|
transition.updateAlpha(node: node, alpha: fraction / fractionSum * alpha)
|
|
}
|
|
}
|
|
}
|
|
}
|