Swiftgram/TelegramUI/RadialStatusNode.swift
2017-08-15 14:44:14 +03:00

187 lines
7.2 KiB
Swift

import Foundation
import AsyncDisplayKit
enum RadialStatusNodeState: Equatable {
case none
case download(UIColor)
case play(UIColor)
case pause(UIColor)
case progress(color: UIColor, value: CGFloat?, cancelEnabled: Bool)
case customIcon(UIImage)
static func ==(lhs: RadialStatusNodeState, rhs: RadialStatusNodeState) -> Bool {
switch lhs {
case .none:
if case .none = rhs {
return true
} else {
return false
}
case let .download(lhsColor):
if case let .download(rhsColor) = rhs, lhsColor.isEqual(rhsColor) {
return true
} else {
return false
}
case let .play(lhsColor):
if case let .play(rhsColor) = rhs, lhsColor.isEqual(rhsColor) {
return true
} else {
return false
}
case let .pause(lhsColor):
if case let .pause(rhsColor) = rhs, lhsColor.isEqual(rhsColor) {
return true
} else {
return false
}
case let .progress(lhsColor, lhsValue, lhsCancelEnabled):
if case let .progress(rhsColor, rhsValue, rhsCancelEnabled) = rhs, lhsColor.isEqual(rhsColor), lhsValue == rhsValue, lhsCancelEnabled == rhsCancelEnabled {
return true
} else {
return false
}
case let .customIcon(lhsImage):
if case let .customIcon(rhsImage) = rhs, lhsImage === rhsImage {
return true
} else {
return false
}
}
}
func backgroundColor(color: UIColor) -> UIColor? {
switch self {
case .none:
return nil
default:
return color
}
}
func contentNode(current: RadialStatusContentNode?) -> RadialStatusContentNode? {
switch self {
case .none:
return nil
case let .download(color):
return RadialStatusIconContentNode(icon: .download(color))
case let .play(color):
return RadialStatusIconContentNode(icon: .play(color))
case let .pause(color):
return RadialStatusIconContentNode(icon: .pause(color))
case let .customIcon(image):
return RadialStatusIconContentNode(icon: .custom(image))
case let .progress(color, value, cancelEnabled):
if let current = current as? RadialProgressContentNode, current.displayCancel == cancelEnabled {
if !current.color.isEqual(color) {
current.color = color
}
current.progress = value
return current
} else {
let node = RadialProgressContentNode(color: color, displayCancel: cancelEnabled)
node.progress = value
return node
}
}
}
}
final class RadialStatusNode: ASControlNode {
private var backgroundNodeColor: UIColor
private var state: RadialStatusNodeState = .none
private var backgroundNode: RadialStatusBackgroundNode?
private var contentNode: RadialStatusContentNode?
private var nextContentNode: RadialStatusContentNode?
init(backgroundNodeColor: UIColor) {
self.backgroundNodeColor = backgroundNodeColor
super.init()
}
func transitionToState(_ state: RadialStatusNodeState, completion: @escaping () -> Void) {
if self.state != state {
self.state = state
let contentNode = state.contentNode(current: self.contentNode)
if contentNode !== self.contentNode {
self.transitionToContentNode(contentNode, backgroundColor: state.backgroundColor(color: self.backgroundNodeColor), completion: completion)
} else {
self.transitionToBackgroundColor(state.backgroundColor(color: self.backgroundNodeColor), completion: completion)
}
}
}
private func transitionToContentNode(_ node: RadialStatusContentNode?, backgroundColor: UIColor?, completion: @escaping () -> Void) {
if let contentNode = self.contentNode {
self.nextContentNode = node
contentNode.enqueueReadyForTransition { [weak contentNode, weak self] in
if let strongSelf = self, let contentNode = contentNode, strongSelf.contentNode === contentNode {
contentNode.animateOut { [weak contentNode] in
contentNode?.removeFromSupernode()
}
strongSelf.contentNode = strongSelf.nextContentNode
if let contentNode = strongSelf.contentNode {
strongSelf.addSubnode(contentNode)
contentNode.frame = strongSelf.bounds
contentNode.layout()
contentNode.animateIn()
}
strongSelf.transitionToBackgroundColor(backgroundColor, completion: completion)
}
}
} else {
self.contentNode = node
if let contentNode = self.contentNode {
contentNode.frame = self.bounds
self.addSubnode(contentNode)
}
self.transitionToBackgroundColor(backgroundColor, completion: completion)
}
}
private func transitionToBackgroundColor(_ color: UIColor?, completion: @escaping () -> Void) {
let currentColor = self.backgroundNode?.color
var updated = false
if let color = color, let currentColor = currentColor {
updated = !color.isEqual(currentColor)
} else if (currentColor != nil) != (color != nil) {
updated = true
}
if updated {
if let color = color {
if let backgroundNode = self.backgroundNode {
backgroundNode.color = color
completion()
} else {
let backgroundNode = RadialStatusBackgroundNode(color: color)
backgroundNode.frame = self.bounds
self.backgroundNode = backgroundNode
self.insertSubnode(backgroundNode, at: 0)
completion()
}
} else if let backgroundNode = self.backgroundNode {
self.backgroundNode = nil
backgroundNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false, completion: { [weak backgroundNode] _ in
backgroundNode?.removeFromSupernode()
completion()
})
}
} else {
completion()
}
}
override func layout() {
self.backgroundNode?.frame = self.bounds
if let contentNode = self.contentNode {
contentNode.frame = self.bounds
}
}
}