mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
333 lines
17 KiB
Swift
333 lines
17 KiB
Swift
import UIKit
|
|
import AsyncDisplayKit
|
|
|
|
internal class NavigationItemWrapper {
|
|
let parentNode: ASDisplayNode
|
|
|
|
private var navigationItem: UINavigationItem
|
|
private var setTitleListenerKey: Int!
|
|
private var setLeftBarButtonItemListenerKey: Int!
|
|
private var setRightBarButtonItemListenerKey: Int!
|
|
|
|
private var previousNavigationItem: UINavigationItem?
|
|
private var previousItemSetTitleListenerKey: Int?
|
|
|
|
private let titleNode: NavigationTitleNode
|
|
private var backButtonNode: NavigationBackButtonNode
|
|
private var leftBarButtonItem: UIBarButtonItem?
|
|
private var leftBarButtonItemWrapper: BarButtonItemWrapper?
|
|
private var rightBarButtonItem: UIBarButtonItem?
|
|
private var rightBarButtonItemWrapper: BarButtonItemWrapper?
|
|
|
|
var backPressed: () -> () = { }
|
|
|
|
var suspendLayout = false
|
|
|
|
init(parentNode: ASDisplayNode, navigationItem: UINavigationItem, previousNavigationItem: UINavigationItem?) {
|
|
self.parentNode = parentNode
|
|
self.navigationItem = navigationItem
|
|
self.previousNavigationItem = previousNavigationItem
|
|
|
|
self.titleNode = NavigationTitleNode(text: "")
|
|
self.parentNode.addSubnode(titleNode)
|
|
|
|
self.backButtonNode = NavigationBackButtonNode()
|
|
backButtonNode.pressed = { [weak self] in
|
|
if let backPressed = self?.backPressed {
|
|
backPressed()
|
|
}
|
|
}
|
|
self.parentNode.addSubnode(self.backButtonNode)
|
|
|
|
self.previousItemSetTitleListenerKey = previousNavigationItem?.addSetTitleListener({ [weak self] title in
|
|
self?.setBackButtonTitle(title)
|
|
return
|
|
})
|
|
|
|
self.setTitleListenerKey = navigationItem.addSetTitleListener({ [weak self] title in
|
|
self?.setTitle(title)
|
|
return
|
|
})
|
|
|
|
self.setLeftBarButtonItemListenerKey = navigationItem.addSetLeftBarButtonItemListener({ [weak self] barButtonItem, animated in
|
|
self?.setLeftBarButtonItem(barButtonItem, animated: animated)
|
|
return
|
|
})
|
|
|
|
self.setRightBarButtonItemListenerKey = navigationItem.addSetRightBarButtonItemListener({ [weak self] barButtonItem, animated in
|
|
self?.setRightBarButtonItem(barButtonItem, animated: animated)
|
|
return
|
|
})
|
|
|
|
self.setTitle(navigationItem.title ?? "")
|
|
self.setBackButtonTitle(previousNavigationItem?.title ?? "Back")
|
|
self.setLeftBarButtonItem(navigationItem.leftBarButtonItem, animated: false)
|
|
self.setRightBarButtonItem(navigationItem.rightBarButtonItem, animated: false)
|
|
}
|
|
|
|
deinit {
|
|
self.navigationItem.removeSetTitleListener(self.setTitleListenerKey)
|
|
self.navigationItem.removeSetLeftBarButtonItemListener(self.setLeftBarButtonItemListenerKey)
|
|
self.navigationItem.removeSetRightBarButtonItemListener(self.setRightBarButtonItemListenerKey)
|
|
|
|
if let previousItemSetTitleListenerKey = self.previousItemSetTitleListenerKey {
|
|
self.previousNavigationItem?.removeSetTitleListener(previousItemSetTitleListenerKey)
|
|
}
|
|
|
|
self.titleNode.removeFromSupernode()
|
|
self.backButtonNode.removeFromSupernode()
|
|
}
|
|
|
|
func setBackButtonTitle(backButtonTitle: String) {
|
|
self.backButtonNode.text = backButtonTitle
|
|
self.layoutItems()
|
|
}
|
|
|
|
func setTitle(title: String) {
|
|
self.titleNode.text = title
|
|
self.layoutItems()
|
|
}
|
|
|
|
func setLeftBarButtonItem(leftBarButtonItem: UIBarButtonItem?, animated: Bool) {
|
|
if self.leftBarButtonItem !== leftBarButtonItem {
|
|
self.leftBarButtonItem = leftBarButtonItem
|
|
|
|
self.leftBarButtonItemWrapper = nil
|
|
|
|
if let leftBarButtonItem = leftBarButtonItem {
|
|
self.leftBarButtonItemWrapper = BarButtonItemWrapper(parentNode: self.parentNode, barButtonItem: leftBarButtonItem, layoutNeeded: { [weak self] in
|
|
self?.layoutItems()
|
|
return
|
|
})
|
|
}
|
|
}
|
|
|
|
self.backButtonNode.hidden = self.previousNavigationItem == nil || self.leftBarButtonItemWrapper != nil
|
|
}
|
|
|
|
func setRightBarButtonItem(rightBarButtonItem: UIBarButtonItem?, animated: Bool) {
|
|
if self.rightBarButtonItem !== rightBarButtonItem {
|
|
self.rightBarButtonItem = rightBarButtonItem
|
|
|
|
self.rightBarButtonItemWrapper = nil
|
|
|
|
if let rightBarButtonItem = rightBarButtonItem {
|
|
self.rightBarButtonItemWrapper = BarButtonItemWrapper(parentNode: self.parentNode, barButtonItem: rightBarButtonItem, layoutNeeded: { [weak self] in
|
|
self?.layoutItems()
|
|
return
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
private var collapsed: Bool {
|
|
get {
|
|
return self.parentNode.frame.size.height < (20.0 + 44.0)
|
|
}
|
|
}
|
|
|
|
var titleFrame: CGRect {
|
|
get {
|
|
return CGRect(x: floor((self.parentNode.frame.size.width - self.titleNode.calculatedSize.width) / 2.0), y: self.collapsed ? 24.0 : 31.0, width: self.titleNode.calculatedSize.width, height: self.titleNode.calculatedSize.height)
|
|
}
|
|
}
|
|
|
|
var titlePosition: CGPoint {
|
|
get {
|
|
let titleFrame = self.titleFrame
|
|
return CGPoint(x: CGRectGetMidX(titleFrame), y: CGRectGetMidY(titleFrame))
|
|
}
|
|
}
|
|
|
|
var backButtonFrame: CGRect {
|
|
get {
|
|
return CGRect(x: self.collapsed ? 15.0 : 8.0, y: self.collapsed ? 24.0 : 31.0, width: backButtonNode.calculatedSize.width, height: backButtonNode.calculatedSize.height)
|
|
}
|
|
}
|
|
|
|
var backButtonLabelFrame: CGRect {
|
|
get {
|
|
let backButtonFrame = self.backButtonFrame
|
|
let labelFrame = self.backButtonNode.labelFrame
|
|
return CGRect(origin: CGPoint(x: backButtonFrame.origin.x + labelFrame.origin.x, y: backButtonFrame.origin.y + labelFrame.origin.y), size: labelFrame.size)
|
|
}
|
|
}
|
|
|
|
var backButtonLabelPosition: CGPoint {
|
|
get {
|
|
let backButtonLabelFrame = self.backButtonLabelFrame
|
|
return CGPoint(x: CGRectGetMidX(backButtonLabelFrame), y: CGRectGetMidY(backButtonLabelFrame))
|
|
}
|
|
}
|
|
|
|
var leftButtonFrame: CGRect? {
|
|
get {
|
|
if let leftBarButtonItemWrapper = self.leftBarButtonItemWrapper {
|
|
return CGRect(x: self.collapsed ? 15.0 : 8.0, y: self.collapsed ? 24.0 : 31.0, width: leftBarButtonItemWrapper.buttonNode.calculatedSize.width, height: leftBarButtonItemWrapper.buttonNode.calculatedSize.height)
|
|
}
|
|
else {
|
|
return nil
|
|
}
|
|
}
|
|
}
|
|
|
|
var rightButtonFrame: CGRect? {
|
|
get {
|
|
if let rightBarButtonItemWrapper = self.rightBarButtonItemWrapper {
|
|
return CGRect(x: self.parentNode.frame.size.width - rightBarButtonItemWrapper.buttonNode.calculatedSize.width - (self.collapsed ? 15.0 : 8.0), y: self.collapsed ? 24.0 : 31.0, width: rightBarButtonItemWrapper.buttonNode.calculatedSize.width, height: rightBarButtonItemWrapper.buttonNode.calculatedSize.height)
|
|
}
|
|
else {
|
|
return nil
|
|
}
|
|
}
|
|
}
|
|
|
|
var transitionState: NavigationItemTransitionState {
|
|
get {
|
|
return NavigationItemTransitionState(backButtonPosition: self.backButtonNode.hidden ? nil : self.backButtonLabelPosition, titlePosition: self.titlePosition)
|
|
}
|
|
}
|
|
|
|
func layoutItems() {
|
|
if suspendLayout {
|
|
return
|
|
}
|
|
self.titleNode.measure(self.parentNode.bounds.size)
|
|
self.titleNode.frame = self.titleFrame
|
|
|
|
self.backButtonNode.measure(self.parentNode.frame.size)
|
|
self.backButtonNode.frame = self.backButtonFrame
|
|
self.backButtonNode.layout()
|
|
|
|
if let leftBarButtonItemWrapper = self.leftBarButtonItemWrapper {
|
|
leftBarButtonItemWrapper.buttonNode.measure(self.parentNode.frame.size)
|
|
leftBarButtonItemWrapper.buttonNode.frame = self.leftButtonFrame!
|
|
}
|
|
|
|
if let rightBarButtonItemWrapper = self.rightBarButtonItemWrapper {
|
|
rightBarButtonItemWrapper.buttonNode.measure(self.parentNode.frame.size)
|
|
rightBarButtonItemWrapper.buttonNode.frame = self.rightButtonFrame!
|
|
}
|
|
}
|
|
|
|
func interpolatePosition(from: CGPoint, _ to: CGPoint, value: CGFloat) -> CGPoint {
|
|
return CGPoint(x: from.x * (CGFloat(1.0) - value) + to.x * value, y: from.y * (CGFloat(1.0) - value) + to.y * value)
|
|
}
|
|
|
|
func interpolateValue(from: CGFloat, _ to: CGFloat, value: CGFloat) -> CGFloat {
|
|
return (from * (CGFloat(1.0) - value)) + (to * value)
|
|
}
|
|
|
|
func applyPushAnimationProgress(previousItemState previousItemState: NavigationItemTransitionState, value: CGFloat) {
|
|
let titleStartPosition = CGPoint(x: self.parentNode.frame.size.width + self.titleNode.frame.size.width / 2.0, y: self.titlePosition.y)
|
|
let titleStartAlpha: CGFloat = 0.0
|
|
let titleEndPosition = self.titlePosition
|
|
let titleEndAlpha: CGFloat = 1.0
|
|
self.titleNode.position = self.interpolatePosition(titleStartPosition, titleEndPosition, value: value)
|
|
self.titleNode.alpha = self.interpolateValue(titleStartAlpha, titleEndAlpha, value: value)
|
|
|
|
self.rightBarButtonItemWrapper?.buttonNode.alpha = self.interpolateValue(0.0, 1.0, value: value)
|
|
self.leftBarButtonItemWrapper?.buttonNode.alpha = self.interpolateValue(0.0, 1.0, value: value)
|
|
|
|
self.backButtonNode.label.position = self.interpolatePosition(CGPoint(x: previousItemState.titlePosition.x - self.backButtonFrame.origin.x, y: previousItemState.titlePosition.y - self.backButtonFrame.origin.y), CGPoint(x: self.backButtonLabelPosition.x - self.backButtonFrame.origin.x, y: self.backButtonLabelPosition.y - self.backButtonFrame.origin.y), value: value)
|
|
self.backButtonNode.alpha = self.interpolateValue(0.0, 1.0, value: value)
|
|
}
|
|
|
|
func applyPushAnimationProgress(nextItemState nextItemState: NavigationItemTransitionState, value: CGFloat) {
|
|
let titleStartPosition = self.titlePosition
|
|
let titleStartAlpha: CGFloat = 1.0
|
|
var titleEndPosition = CGPoint(x: -self.titleNode.frame.size.width / 2.0, y: self.titlePosition.y)
|
|
if let nextItemBackButtonPosition = nextItemState.backButtonPosition {
|
|
titleEndPosition = nextItemBackButtonPosition
|
|
}
|
|
let titleEndAlpha: CGFloat = 0.0
|
|
|
|
self.titleNode.position = self.interpolatePosition(titleStartPosition, titleEndPosition, value: value)
|
|
self.titleNode.alpha = self.interpolateValue(titleStartAlpha, titleEndAlpha, value: value)
|
|
|
|
self.rightBarButtonItemWrapper?.buttonNode.alpha = self.interpolateValue(1.0, 0.0, value: value)
|
|
self.leftBarButtonItemWrapper?.buttonNode.alpha = self.interpolateValue(1.0, 0.0, value: value)
|
|
|
|
self.backButtonNode.label.position = self.interpolatePosition(CGPoint(x: self.backButtonLabelPosition.x - self.backButtonFrame.origin.x, y: self.backButtonLabelPosition.y - self.backButtonFrame.origin.y), CGPoint(x: -self.backButtonLabelFrame.size.width - self.backButtonFrame.origin.x, y: self.backButtonLabelPosition.y - self.backButtonFrame.origin.y), value: value)
|
|
self.backButtonNode.label.alpha = self.interpolateValue(1.0, 0.0, value: value)
|
|
self.backButtonNode.arrow.alpha = self.interpolateValue(1.0, nextItemState.backButtonPosition == nil ? 0.0 : 1.0, value: value)
|
|
}
|
|
|
|
func applyPopAnimationProgress(previousItemState previousItemState: NavigationItemTransitionState, value: CGFloat) {
|
|
var titleStartPosition = CGPoint(x: -self.titleNode.frame.size.width / 2.0, y: self.titlePosition.y)
|
|
if let previousItemBackButtonPosition = previousItemState.backButtonPosition {
|
|
titleStartPosition = previousItemBackButtonPosition
|
|
}
|
|
let titleStartAlpha: CGFloat = 0.0
|
|
let titleEndPosition = self.titlePosition
|
|
let titleEndAlpha: CGFloat = 1.0
|
|
self.titleNode.position = self.interpolatePosition(titleStartPosition, titleEndPosition, value: value)
|
|
self.titleNode.alpha = self.interpolateValue(titleStartAlpha, titleEndAlpha, value: value)
|
|
|
|
self.rightBarButtonItemWrapper?.buttonNode.alpha = self.interpolateValue(0.0, 1.0, value: value)
|
|
self.leftBarButtonItemWrapper?.buttonNode.alpha = self.interpolateValue(0.0, 1.0, value: value)
|
|
|
|
self.backButtonNode.label.position = self.interpolatePosition(CGPoint(x: -self.backButtonLabelFrame.size.width - self.backButtonFrame.origin.x, y: self.backButtonLabelPosition.y - self.backButtonFrame.origin.y), CGPoint(x: self.backButtonLabelPosition.x - self.backButtonFrame.origin.x, y: self.backButtonLabelPosition.y - self.backButtonFrame.origin.y), value: value)
|
|
self.backButtonNode.label.alpha = self.interpolateValue(0.0, 1.0, value: value)
|
|
self.backButtonNode.arrow.alpha = self.interpolateValue(previousItemState.backButtonPosition == nil ? 0.0 : 1.0, 1.0, value: value)
|
|
}
|
|
|
|
func applyPopAnimationProgress(nextItemState nextItemState: NavigationItemTransitionState, value: CGFloat) {
|
|
let titleStartPosition = self.titlePosition
|
|
let titleStartAlpha: CGFloat = 1.0
|
|
let titleEndPosition = CGPoint(x: self.parentNode.frame.size.width + self.titleNode.frame.size.width / 2.0, y: self.titlePosition.y)
|
|
let titleEndAlpha: CGFloat = 0.0
|
|
self.titleNode.position = self.interpolatePosition(titleStartPosition, titleEndPosition, value: value)
|
|
self.titleNode.alpha = self.interpolateValue(titleStartAlpha, titleEndAlpha, value: value)
|
|
|
|
self.rightBarButtonItemWrapper?.buttonNode.alpha = self.interpolateValue(1.0, 0.0, value: value)
|
|
self.leftBarButtonItemWrapper?.buttonNode.alpha = self.interpolateValue(1.0, 0.0, value: value)
|
|
|
|
self.backButtonNode.label.position = self.interpolatePosition(CGPoint(x: self.backButtonLabelPosition.x - self.backButtonFrame.origin.x, y: self.backButtonLabelPosition.y - self.backButtonFrame.origin.y), CGPoint(x: nextItemState.titlePosition.x - self.backButtonFrame.origin.x, y: nextItemState.titlePosition.y - self.backButtonFrame.origin.y), value: value)
|
|
self.backButtonNode.label.alpha = self.interpolateValue(1.0, 0.0, value: value)
|
|
self.backButtonNode.arrow.alpha = self.interpolateValue(1.0, 0.0, value: value)
|
|
}
|
|
|
|
func animatePush(previousItemWrapper: NavigationItemWrapper?, duration: NSTimeInterval) {
|
|
if let previousItemWrapper = previousItemWrapper {
|
|
self.suspendLayout = true
|
|
self.backButtonNode.suspendLayout = true
|
|
|
|
let transitionState = self.transitionState
|
|
let previousItemState = previousItemWrapper.transitionState
|
|
|
|
self.applyPushAnimationProgress(previousItemState: previousItemState, value: 0.0)
|
|
previousItemWrapper.applyPushAnimationProgress(nextItemState: transitionState, value: 0.0)
|
|
|
|
UIView.animateWithDuration(duration, delay: 0.0, options: UIViewAnimationOptions(rawValue: 7 << 16), animations: { () -> Void in
|
|
self.applyPushAnimationProgress(previousItemState: previousItemState, value: 1.0)
|
|
previousItemWrapper.applyPushAnimationProgress(nextItemState: transitionState, value: 1.0)
|
|
}, completion: { completed in
|
|
self.suspendLayout = false
|
|
self.backButtonNode.suspendLayout = false
|
|
|
|
previousItemWrapper.applyPushAnimationProgress(nextItemState: self.transitionState, value: 1.0)
|
|
})
|
|
}
|
|
}
|
|
|
|
func animatePop(previousItemWrapper: NavigationItemWrapper?, duration: NSTimeInterval) {
|
|
if let previousItemWrapper = previousItemWrapper {
|
|
self.applyPopAnimationProgress(previousItemState: previousItemWrapper.transitionState, value: 0.0)
|
|
previousItemWrapper.applyPopAnimationProgress(nextItemState: self.transitionState, value: 0.0)
|
|
|
|
UIView.animateWithDuration(duration, delay: 0.0, options: UIViewAnimationOptions(rawValue: 7 << 16), animations: { () -> Void in
|
|
self.applyPopAnimationProgress(previousItemState: previousItemWrapper.transitionState, value: 1.0)
|
|
previousItemWrapper.applyPopAnimationProgress(nextItemState: self.transitionState, value: 1.0)
|
|
}, completion: { completed in
|
|
previousItemWrapper.applyPopAnimationProgress(nextItemState: self.transitionState, value: 0.0)
|
|
})
|
|
}
|
|
}
|
|
|
|
func setInteractivePopProgress(progress: CGFloat, previousItemWrapper: NavigationItemWrapper) {
|
|
self.applyPopAnimationProgress(previousItemState: previousItemWrapper.transitionState, value: progress)
|
|
previousItemWrapper.applyPopAnimationProgress(nextItemState: self.transitionState, value: progress)
|
|
}
|
|
}
|