mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-15 21:45:19 +00:00
172 lines
6.7 KiB
Swift
172 lines
6.7 KiB
Swift
import Foundation
|
|
import UIKit
|
|
import AsyncDisplayKit
|
|
import Display
|
|
import TelegramPresentationData
|
|
import ManagedAnimationNode
|
|
import ContextUI
|
|
|
|
public final class MoreButtonNode: ASDisplayNode {
|
|
public class MoreIconNode: ManagedAnimationNode {
|
|
public enum State: Equatable {
|
|
case more
|
|
case search
|
|
}
|
|
|
|
private let encircled: Bool
|
|
private let duration: Double = 0.21
|
|
public var iconState: State = .search
|
|
|
|
init(size: CGSize = CGSize(width: 30.0, height: 30.0), encircled: Bool) {
|
|
self.encircled = encircled
|
|
|
|
super.init(size: size)
|
|
|
|
if self.encircled {
|
|
self.trackTo(item: ManagedAnimationItem(source: .local("anim_moretosearch"), frames: .range(startFrame: 90, endFrame: 90), duration: 0.0))
|
|
} else {
|
|
self.iconState = .more
|
|
self.trackTo(item: ManagedAnimationItem(source: .local("anim_baremoredots"), frames: .range(startFrame: 0, endFrame: 0), duration: 0.0))
|
|
}
|
|
}
|
|
|
|
func play() {
|
|
if case .more = self.iconState {
|
|
let animationName = self.encircled ? "anim_moredots" : "anim_baremoredots"
|
|
self.trackTo(item: ManagedAnimationItem(source: .local(animationName), frames: .range(startFrame: 0, endFrame: 46), duration: 0.76))
|
|
}
|
|
}
|
|
|
|
public func enqueueState(_ state: State, animated: Bool) {
|
|
guard self.iconState != state else {
|
|
return
|
|
}
|
|
|
|
let previousState = self.iconState
|
|
self.iconState = state
|
|
|
|
let source = ManagedAnimationSource.local("anim_moretosearch")
|
|
|
|
let totalLength: Int = 90
|
|
if animated {
|
|
switch previousState {
|
|
case .more:
|
|
switch state {
|
|
case .more:
|
|
break
|
|
case .search:
|
|
self.trackTo(item: ManagedAnimationItem(source: source, frames: .range(startFrame: 0, endFrame: totalLength), duration: self.duration))
|
|
}
|
|
case .search:
|
|
switch state {
|
|
case .more:
|
|
self.trackTo(item: ManagedAnimationItem(source: source, frames: .range(startFrame: totalLength, endFrame: 0), duration: self.duration))
|
|
case .search:
|
|
break
|
|
}
|
|
}
|
|
} else {
|
|
switch state {
|
|
case .more:
|
|
self.trackTo(item: ManagedAnimationItem(source: source, frames: .range(startFrame: 0, endFrame: 0), duration: 0.0))
|
|
case .search:
|
|
self.trackTo(item: ManagedAnimationItem(source: source, frames: .range(startFrame: totalLength, endFrame: totalLength), duration: 0.0))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public var action: ((ASDisplayNode, ContextGesture?) -> Void)?
|
|
|
|
private let containerNode: ContextControllerSourceNode
|
|
public let contextSourceNode: ContextReferenceContentNode
|
|
private let buttonNode: HighlightableButtonNode
|
|
public let iconNode: MoreIconNode
|
|
|
|
private var color: UIColor?
|
|
|
|
public var theme: PresentationTheme {
|
|
didSet {
|
|
self.update()
|
|
}
|
|
}
|
|
private let size: CGSize
|
|
|
|
public func updateColor(_ color: UIColor?, transition: ContainedViewLayoutTransition) {
|
|
self.color = color
|
|
|
|
if case let .animated(duration, curve) = transition {
|
|
if let snapshotView = self.iconNode.view.snapshotContentTree() {
|
|
snapshotView.frame = self.iconNode.frame
|
|
self.view.addSubview(snapshotView)
|
|
|
|
snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: duration, timingFunction: curve.timingFunction, removeOnCompletion: false, completion: { _ in
|
|
snapshotView.removeFromSuperview()
|
|
})
|
|
self.iconNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: duration, timingFunction: curve.timingFunction)
|
|
}
|
|
}
|
|
self.update()
|
|
}
|
|
|
|
private func update() {
|
|
let color = self.color ?? self.theme.rootController.navigationBar.buttonColor
|
|
self.iconNode.customColor = color
|
|
}
|
|
|
|
public init(theme: PresentationTheme, size: CGSize = CGSize(width: 30.0, height: 30.0), encircled: Bool = true) {
|
|
self.theme = theme
|
|
self.size = size
|
|
|
|
self.contextSourceNode = ContextReferenceContentNode()
|
|
self.containerNode = ContextControllerSourceNode()
|
|
self.containerNode.animateScale = false
|
|
|
|
self.buttonNode = HighlightableButtonNode()
|
|
self.iconNode = MoreIconNode(size: size, encircled: encircled)
|
|
self.iconNode.customColor = self.theme.rootController.navigationBar.buttonColor
|
|
|
|
super.init()
|
|
|
|
self.addSubnode(self.buttonNode)
|
|
|
|
self.buttonNode.addSubnode(self.containerNode)
|
|
self.containerNode.addSubnode(self.contextSourceNode)
|
|
self.contextSourceNode.addSubnode(self.iconNode)
|
|
|
|
self.buttonNode.addTarget(self, action: #selector(self.buttonPressed), forControlEvents: .touchUpInside)
|
|
|
|
self.containerNode.activated = { [weak self] gesture, _ in
|
|
guard let strongSelf = self else {
|
|
return
|
|
}
|
|
if case .more = strongSelf.iconNode.iconState {
|
|
strongSelf.action?(strongSelf.contextSourceNode, gesture)
|
|
}
|
|
}
|
|
}
|
|
|
|
@objc public func buttonPressed() {
|
|
self.action?(self.contextSourceNode, nil)
|
|
if case .more = self.iconNode.iconState {
|
|
self.iconNode.play()
|
|
}
|
|
}
|
|
|
|
override public func calculateSizeThatFits(_ constrainedSize: CGSize) -> CGSize {
|
|
let animationSize = self.size
|
|
let inset: CGFloat = 0.0
|
|
let iconFrame = CGRect(origin: CGPoint(x: inset + 6.0, y: floor((constrainedSize.height - animationSize.height) / 2.0) + 1.0), size: animationSize)
|
|
|
|
self.iconNode.position = iconFrame.center
|
|
self.iconNode.bounds = CGRect(origin: .zero, size: iconFrame.size)
|
|
|
|
let size = CGSize(width: animationSize.width + inset * 2.0, height: constrainedSize.height)
|
|
let bounds = CGRect(origin: CGPoint(), size: size)
|
|
self.buttonNode.frame = bounds
|
|
self.containerNode.frame = bounds
|
|
self.contextSourceNode.frame = bounds
|
|
return size
|
|
}
|
|
}
|