import Foundation import UIKit import AsyncDisplayKit import Display import TelegramPresentationData public class WebAppCancelButtonNode: ASDisplayNode { public enum State { case cancel case back } public let buttonNode: HighlightTrackingButtonNode private let arrowNode: ASImageNode private let labelNode: ImmediateTextNode public var state: State = .cancel private var color: UIColor? private var _theme: PresentationTheme public var theme: PresentationTheme { get { return self._theme } set { self._theme = newValue self.setState(self.state, animated: false, animateScale: false, force: true) } } private let strings: PresentationStrings private weak var colorSnapshotView: UIView? public func updateColor(_ color: UIColor?, transition: ContainedViewLayoutTransition) { let previousColor = self.color self.color = color if case let .animated(duration, curve) = transition, previousColor != color, !self.animatingStateChange { if let snapshotView = self.view.snapshotContentTree() { snapshotView.frame = self.bounds self.view.addSubview(snapshotView) self.colorSnapshotView = snapshotView snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: duration, timingFunction: curve.timingFunction, removeOnCompletion: false, completion: { _ in snapshotView.removeFromSuperview() }) self.arrowNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: duration, timingFunction: curve.timingFunction) self.labelNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: duration, timingFunction: curve.timingFunction) } } self.setState(self.state, animated: false, animateScale: false, force: true) } public init(theme: PresentationTheme, strings: PresentationStrings) { self._theme = theme self.strings = strings self.buttonNode = HighlightTrackingButtonNode() self.arrowNode = ASImageNode() self.arrowNode.displaysAsynchronously = false self.labelNode = ImmediateTextNode() self.labelNode.displaysAsynchronously = false super.init() self.addSubnode(self.buttonNode) self.buttonNode.addSubnode(self.arrowNode) self.buttonNode.addSubnode(self.labelNode) self.buttonNode.highligthedChanged = { [weak self] highlighted in guard let strongSelf = self else { return } if highlighted { strongSelf.arrowNode.layer.removeAnimation(forKey: "opacity") strongSelf.arrowNode.alpha = 0.4 strongSelf.labelNode.layer.removeAnimation(forKey: "opacity") strongSelf.labelNode.alpha = 0.4 } else { strongSelf.arrowNode.alpha = 1.0 strongSelf.arrowNode.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2) strongSelf.labelNode.alpha = 1.0 strongSelf.labelNode.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2) } } self.setState(.cancel, animated: false, force: true) } public func setTheme(_ theme: PresentationTheme, animated: Bool) { self._theme = theme var animated = animated if self.animatingStateChange { animated = false } self.setState(self.state, animated: animated, animateScale: false, force: true) } private var animatingStateChange = false public func setState(_ state: State, animated: Bool, animateScale: Bool = true, force: Bool = false) { guard self.state != state || force else { return } self.state = state if let colorSnapshotView = self.colorSnapshotView { self.colorSnapshotView = nil colorSnapshotView.removeFromSuperview() } if animated, let snapshotView = self.buttonNode.view.snapshotContentTree() { self.animatingStateChange = true snapshotView.layer.sublayerTransform = self.buttonNode.subnodeTransform self.view.addSubview(snapshotView) let duration: Double = animateScale ? 0.25 : 0.3 if animateScale { snapshotView.layer.animateScale(from: 1.0, to: 0.001, duration: 0.25, removeOnCompletion: false) } snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: duration, removeOnCompletion: false, completion: { [weak snapshotView] _ in snapshotView?.removeFromSuperview() self.animatingStateChange = false }) if animateScale { self.buttonNode.layer.animateScale(from: 0.001, to: 1.0, duration: 0.25) } self.buttonNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: duration) } let color = self.color ?? self.theme.rootController.navigationBar.accentTextColor self.arrowNode.isHidden = state == .cancel self.labelNode.attributedText = NSAttributedString(string: state == .cancel ? self.strings.Common_Close : self.strings.Common_Back, font: Font.regular(17.0), textColor: color) let labelSize = self.labelNode.updateLayout(CGSize(width: 120.0, height: 56.0)) self.buttonNode.frame = CGRect(origin: .zero, size: CGSize(width: labelSize.width, height: self.buttonNode.frame.height)) self.arrowNode.image = NavigationBarTheme.generateBackArrowImage(color: color) if let image = self.arrowNode.image { self.arrowNode.frame = CGRect(origin: self.arrowNode.frame.origin, size: image.size) } self.labelNode.frame = CGRect(origin: self.labelNode.frame.origin, size: labelSize) self.buttonNode.subnodeTransform = CATransform3DMakeTranslation(state == .back ? 11.0 : 0.0, 0.0, 0.0) } override public func calculateSizeThatFits(_ constrainedSize: CGSize) -> CGSize { self.buttonNode.frame = CGRect(origin: .zero, size: CGSize(width: self.buttonNode.frame.width, height: constrainedSize.height)) self.arrowNode.frame = CGRect(origin: CGPoint(x: -19.0, y: floorToScreenPixels((constrainedSize.height - self.arrowNode.frame.size.height) / 2.0)), size: self.arrowNode.frame.size) self.labelNode.frame = CGRect(origin: CGPoint(x: 0.0, y: floorToScreenPixels((constrainedSize.height - self.labelNode.frame.size.height) / 2.0)), size: self.labelNode.frame.size) return CGSize(width: 70.0, height: 56.0) } }