Swiftgram/submodules/PremiumUI/Sources/RollingCountLabel.swift
2022-05-11 19:13:58 +04:00

198 lines
6.1 KiB
Swift

import UIKit
private extension UILabel {
func textWidth() -> CGFloat {
return UILabel.textWidth(label: self)
}
class func textWidth(label: UILabel) -> CGFloat {
return textWidth(label: label, text: label.text!)
}
class func textWidth(label: UILabel, text: String) -> CGFloat {
return textWidth(font: label.font, text: text)
}
class func textWidth(font: UIFont, text: String) -> CGFloat {
let myText = text as NSString
let rect = CGSize(width: CGFloat.greatestFiniteMagnitude, height: CGFloat.greatestFiniteMagnitude)
let labelSize = myText.boundingRect(with: rect, options: .usesLineFragmentOrigin, attributes: [NSAttributedString.Key.font: font], context: nil)
return ceil(labelSize.width)
}
}
open class RollingLabel: UILabel {
private var fullText = ""
private var suffix: String = ""
open var showSymbol = false
private var scrollLayers: [CAScrollLayer] = []
private var scrollLabels: [UILabel] = []
private let duration = 1.12
private let durationOffset = 0.2
private let textsNotAnimated = [","]
public func text(num: Int) {
self.configure(with: num)
self.text = " "
self.animate()
}
public func setPrefix(prefix: String) {
self.suffix = prefix
}
private func configure(with number: Int) {
fullText = String(number)
clean()
setupSubviews()
}
private func animate(ascending: Bool = true) {
createAnimations(ascending: ascending)
}
private func clean() {
self.text = nil
self.subviews.forEach { $0.removeFromSuperview() }
self.layer.sublayers?.forEach { $0.removeFromSuperlayer() }
scrollLayers.removeAll()
scrollLabels.removeAll()
}
private func setupSubviews() {
let stringArray = fullText.map { String($0) }
var x: CGFloat = 0
let y: CGFloat = 0
if self.textAlignment == .center {
if showSymbol {
self.text = "\(fullText) \(suffix)"
} else {
self.text = fullText
}
let w = UILabel.textWidth(font: self.font, text: self.text ?? "")
self.text = ""
x = -(w / 2)
} else if self.textAlignment == .right {
if showSymbol {
self.text = "\(fullText) \(suffix) "
} else {
self.text = fullText
}
let w = UILabel.textWidth(font: self.font, text: self.text ?? "")
self.text = ""
x = -w
}
if showSymbol {
let wLabel = UILabel()
wLabel.frame.origin = CGPoint(x: x, y: y)
wLabel.textColor = textColor
wLabel.font = font
wLabel.text = "\(suffix) "
wLabel.textAlignment = .center
wLabel.sizeToFit()
self.addSubview(wLabel)
x += wLabel.bounds.width
}
stringArray.enumerated().forEach { index, text in
if textsNotAnimated.contains(text) {
let label = UILabel()
label.frame.origin = CGPoint(x: x, y: y)
label.textColor = textColor
label.font = font
label.text = text
label.textAlignment = .center
label.sizeToFit()
self.addSubview(label)
x += label.bounds.width
} else {
let label = UILabel()
label.frame.origin = CGPoint(x: x, y: y)
label.textColor = textColor
label.font = font
label.text = "0"
label.textAlignment = .center
label.sizeToFit()
createScrollLayer(to: label, text: text)
x += label.bounds.width
}
}
}
private func createScrollLayer(to label: UILabel, text: String) {
let scrollLayer = CAScrollLayer()
scrollLayer.frame = label.frame
scrollLayers.append(scrollLayer)
self.layer.addSublayer(scrollLayer)
createContentForLayer(scrollLayer: scrollLayer, text: text)
}
private func createContentForLayer(scrollLayer: CAScrollLayer, text: String) {
var textsForScroll: [String] = []
let max: Int
var found = false
if let val = Int(text) {
max = val
found = true
} else {
max = 9
}
for i in 0...max {
let str = String(i)
textsForScroll.append(str)
}
if !found {
textsForScroll.append(text)
}
var height: CGFloat = 0
for text in textsForScroll {
let label = UILabel()
label.text = text
label.textColor = textColor
label.font = font
label.textAlignment = .center
label.frame = CGRect(x: 0, y: height, width: scrollLayer.frame.width, height: scrollLayer.frame.height)
scrollLayer.addSublayer(label.layer)
scrollLabels.append(label)
height = label.frame.maxY
}
}
private func createAnimations(ascending: Bool) {
var offset: CFTimeInterval = 0.0
for scrollLayer in scrollLayers {
let maxY = scrollLayer.sublayers?.last?.frame.origin.y ?? 0.0
let animation = CABasicAnimation(keyPath: "sublayerTransform.translation.y")
animation.duration = duration + offset
animation.timingFunction = CAMediaTimingFunction(name: .easeOut)
if ascending {
animation.fromValue = maxY
animation.toValue = 0
} else {
animation.fromValue = 0
animation.toValue = maxY
}
scrollLayer.scrollMode = .vertically
scrollLayer.add(animation, forKey: nil)
scrollLayer.scroll(to: CGPoint(x: 0, y: maxY))
offset += self.durationOffset
}
}
}