mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-07-19 17:51:29 +00:00
89 lines
3.5 KiB
Swift
89 lines
3.5 KiB
Swift
import Foundation
|
|
import UIKit
|
|
import ComponentFlow
|
|
|
|
final class TextView: UIView {
|
|
private struct Params: Equatable {
|
|
var string: String
|
|
var fontSize: CGFloat
|
|
var fontWeight: CGFloat
|
|
var monospacedDigits: Bool
|
|
var alignment: NSTextAlignment
|
|
var constrainedWidth: CGFloat
|
|
}
|
|
|
|
private struct LayoutState: Equatable {
|
|
var params: Params
|
|
var size: CGSize
|
|
var attributedString: NSAttributedString
|
|
}
|
|
|
|
private var layoutState: LayoutState?
|
|
private var animateContentsTransition: Bool = false
|
|
|
|
override init(frame: CGRect) {
|
|
super.init(frame: CGRect())
|
|
|
|
self.isOpaque = false
|
|
self.backgroundColor = nil
|
|
}
|
|
|
|
required init?(coder: NSCoder) {
|
|
fatalError("init(coder:) has not been implemented")
|
|
}
|
|
|
|
override func action(for layer: CALayer, forKey event: String) -> CAAction? {
|
|
if self.animateContentsTransition && event == "contents" {
|
|
self.animateContentsTransition = false
|
|
let animation = CABasicAnimation(keyPath: "contents")
|
|
animation.duration = 0.15 * UIView.animationDurationFactor()
|
|
animation.timingFunction = CAMediaTimingFunction(name: .linear)
|
|
return animation
|
|
}
|
|
return super.action(for: layer, forKey: event)
|
|
}
|
|
|
|
func update(string: String, fontSize: CGFloat, fontWeight: CGFloat, monospacedDigits: Bool = false, alignment: NSTextAlignment = .natural, color: UIColor, constrainedWidth: CGFloat, transition: ComponentTransition) -> CGSize {
|
|
let params = Params(string: string, fontSize: fontSize, fontWeight: fontWeight, monospacedDigits: monospacedDigits, alignment: alignment, constrainedWidth: constrainedWidth)
|
|
if let layoutState = self.layoutState, layoutState.params == params {
|
|
return layoutState.size
|
|
}
|
|
|
|
let font: UIFont
|
|
if monospacedDigits {
|
|
font = UIFont.monospacedDigitSystemFont(ofSize: fontSize, weight: UIFont.Weight(fontWeight))
|
|
} else {
|
|
font = UIFont.systemFont(ofSize: fontSize, weight: UIFont.Weight(fontWeight))
|
|
}
|
|
|
|
let paragraphStyle = NSMutableParagraphStyle()
|
|
paragraphStyle.alignment = alignment
|
|
paragraphStyle.lineSpacing = 0.6
|
|
let attributedString = NSAttributedString(string: string, attributes: [
|
|
.font: font,
|
|
.foregroundColor: color,
|
|
.paragraphStyle: paragraphStyle
|
|
])
|
|
let stringBounds = attributedString.boundingRect(with: CGSize(width: constrainedWidth, height: 200.0), options: .usesLineFragmentOrigin, context: nil)
|
|
let stringSize = CGSize(width: ceil(stringBounds.width), height: ceil(stringBounds.height))
|
|
let size = CGSize(width: min(constrainedWidth, stringSize.width), height: stringSize.height)
|
|
|
|
let layoutState = LayoutState(params: params, size: size, attributedString: attributedString)
|
|
if self.layoutState != layoutState {
|
|
self.layoutState = layoutState
|
|
self.animateContentsTransition = !transition.animation.isImmediate
|
|
self.setNeedsDisplay()
|
|
}
|
|
|
|
return size
|
|
}
|
|
|
|
override func draw(_ rect: CGRect) {
|
|
guard let layoutState = self.layoutState else {
|
|
return
|
|
}
|
|
|
|
layoutState.attributedString.draw(with: rect, options: [.truncatesLastVisibleLine, .usesLineFragmentOrigin], context: nil)
|
|
}
|
|
}
|