Swiftgram/submodules/Display/Sources/NavigationBackButtonNode.swift
2020-03-01 13:59:30 +04:00

151 lines
5.7 KiB
Swift

import UIKit
import AsyncDisplayKit
import AppBundle
public class NavigationBackButtonNode: ASControlNode {
private func fontForCurrentState() -> UIFont {
return UIFont.systemFont(ofSize: 17.0)
}
private func attributesForCurrentState() -> [NSAttributedString.Key : AnyObject] {
return [
NSAttributedString.Key.font: self.fontForCurrentState(),
NSAttributedString.Key.foregroundColor: self.isEnabled ? self.color : self.disabledColor
]
}
let arrow: ASDisplayNode
let label: ASTextNode
private let arrowSpacing: CGFloat = 4.0
private var _text: String = ""
public var text: String {
get {
return self._text
}
set(value) {
self._text = value
self.label.attributedText = NSAttributedString(string: text, attributes: self.attributesForCurrentState())
self.invalidateCalculatedLayout()
}
}
public var color: UIColor = UIColor(rgb: 0x007ee5) {
didSet {
self.label.attributedText = NSAttributedString(string: self._text, attributes: self.attributesForCurrentState())
}
}
public var disabledColor: UIColor = UIColor(rgb: 0xd0d0d0) {
didSet {
self.label.attributedText = NSAttributedString(string: self._text, attributes: self.attributesForCurrentState())
}
}
private var touchCount = 0
var pressed: () -> () = {}
override public init() {
self.arrow = ASDisplayNode()
self.label = ASTextNode()
super.init()
self.isUserInteractionEnabled = true
self.isExclusiveTouch = true
self.hitTestSlop = UIEdgeInsets(top: -16.0, left: -10.0, bottom: -16.0, right: -10.0)
self.displaysAsynchronously = false
self.arrow.displaysAsynchronously = false
self.label.displaysAsynchronously = false
self.addSubnode(self.arrow)
let arrowImage = UIImage(named: "NavigationBackArrowLight", in: getAppBundle(), compatibleWith: nil)?.precomposed()
self.arrow.contents = arrowImage?.cgImage
self.arrow.frame = CGRect(origin: CGPoint(), size: arrowImage?.size ?? CGSize())
self.addSubnode(self.label)
}
public override func calculateSizeThatFits(_ constrainedSize: CGSize) -> CGSize {
self.label.measure(CGSize(width: max(0.0, constrainedSize.width - self.arrow.frame.size.width - self.arrowSpacing), height: constrainedSize.height))
return CGSize(width: self.arrow.frame.size.width + self.arrowSpacing + self.label.calculatedSize.width, height: max(self.arrow.frame.size.height, self.label.calculatedSize.height))
}
var labelFrame: CGRect {
get {
return CGRect(x: self.arrow.frame.size.width + self.arrowSpacing, y: floor((self.frame.size.height - self.label.calculatedSize.height) / 2.0), width: self.label.calculatedSize.width, height: self.label.calculatedSize.height)
}
}
public override func layout() {
super.layout()
self.arrow.frame = CGRect(x: 0.0, y: floor((self.frame.size.height - arrow.frame.size.height) / 2.0), width: self.arrow.frame.size.width, height: self.arrow.frame.size.height)
self.label.frame = self.labelFrame
}
private func touchInsideApparentBounds(_ touch: UITouch) -> Bool {
var apparentBounds = self.bounds
let hitTestSlop = self.hitTestSlop
apparentBounds.origin.x += hitTestSlop.left
apparentBounds.size.width -= hitTestSlop.left + hitTestSlop.right
apparentBounds.origin.y += hitTestSlop.top
apparentBounds.size.height -= hitTestSlop.top + hitTestSlop.bottom
return apparentBounds.contains(touch.location(in: self.view))
}
public override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesBegan(touches, with: event)
self.touchCount += touches.count
self.updateHighlightedState(true, animated: false)
}
public override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesMoved(touches, with: event)
self.updateHighlightedState(self.touchInsideApparentBounds(touches.first!), animated: true)
}
public override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesEnded(touches, with: event)
self.updateHighlightedState(false, animated: false)
let previousTouchCount = self.touchCount
self.touchCount = max(0, self.touchCount - touches.count)
if previousTouchCount != 0 && self.touchCount == 0 && self.isEnabled && self.touchInsideApparentBounds(touches.first!) {
self.pressed()
}
}
public override func touchesCancelled(_ touches: Set<UITouch>?, with event: UIEvent?) {
super.touchesCancelled(touches, with: event)
self.touchCount = max(0, self.touchCount - (touches?.count ?? 0))
self.updateHighlightedState(false, animated: false)
}
private var _highlighted = false
private func updateHighlightedState(_ highlighted: Bool, animated: Bool) {
if _highlighted != highlighted {
_highlighted = highlighted
let alpha: CGFloat = !self.isEnabled ? 1.0 : (highlighted ? 0.4 : 1.0)
if animated {
UIView.animate(withDuration: 0.3, delay: 0.0, options: UIView.AnimationOptions.beginFromCurrentState, animations: { () -> Void in
self.alpha = alpha
}, completion: nil)
}
else {
self.alpha = alpha
}
}
}
}