mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-01 12:17:53 +00:00
144 lines
7.3 KiB
Swift
144 lines
7.3 KiB
Swift
import Foundation
|
|
import AsyncDisplayKit
|
|
import Display
|
|
import TelegramCore
|
|
|
|
final class MediaNavigationAccessoryContainerNode: ASDisplayNode, UIGestureRecognizerDelegate {
|
|
let backgroundNode: ASDisplayNode
|
|
let headerNode: MediaNavigationAccessoryHeaderNode
|
|
let itemListNode: MediaNavigationAccessoryItemListNode
|
|
|
|
private var currentHeaderHeight: CGFloat = MediaNavigationAccessoryHeaderNode.minimizedHeight
|
|
private var draggingHeaderHeight: CGFloat?
|
|
private var effectiveHeaderHeight: CGFloat {
|
|
if let draggingHeaderHeight = self.draggingHeaderHeight {
|
|
return draggingHeaderHeight
|
|
} else {
|
|
return self.currentHeaderHeight
|
|
}
|
|
}
|
|
|
|
private var presentationData: PresentationData
|
|
|
|
init(account: Account) {
|
|
self.presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 }
|
|
|
|
self.backgroundNode = ASDisplayNode()
|
|
self.headerNode = MediaNavigationAccessoryHeaderNode(theme: self.presentationData.theme, strings: self.presentationData.strings)
|
|
self.itemListNode = MediaNavigationAccessoryItemListNode(account: account)
|
|
|
|
super.init()
|
|
|
|
self.backgroundNode.backgroundColor = self.presentationData.theme.rootController.navigationBar.backgroundColor
|
|
self.addSubnode(self.backgroundNode)
|
|
|
|
self.addSubnode(self.itemListNode)
|
|
self.addSubnode(self.headerNode)
|
|
|
|
self.headerNode.expand = { [weak self] in
|
|
if let strongSelf = self, strongSelf.draggingHeaderHeight == nil {
|
|
let middleHeight = MediaNavigationAccessoryHeaderNode.maximizedHeight + MediaNavigationAccessoryItemListNode.minimizedPanelHeight
|
|
strongSelf.currentHeaderHeight = middleHeight
|
|
strongSelf.updateLayout(size: strongSelf.bounds.size, transition: .animated(duration: 0.3, curve: .spring))
|
|
}
|
|
}
|
|
|
|
self.itemListNode.collapse = { [weak self] in
|
|
if let strongSelf = self, strongSelf.draggingHeaderHeight == nil {
|
|
let middleHeight = MediaNavigationAccessoryHeaderNode.maximizedHeight + MediaNavigationAccessoryItemListNode.minimizedPanelHeight
|
|
if middleHeight.isLess(than: strongSelf.currentHeaderHeight) {
|
|
strongSelf.currentHeaderHeight = middleHeight
|
|
} else {
|
|
strongSelf.currentHeaderHeight = strongSelf.bounds.size.height
|
|
}
|
|
strongSelf.updateLayout(size: strongSelf.bounds.size, transition: .animated(duration: 0.3, curve: .spring))
|
|
}
|
|
}
|
|
}
|
|
|
|
override func didLoad() {
|
|
super.didLoad()
|
|
|
|
let panRecognizer = UIPanGestureRecognizer(target: self, action: #selector(self.panGesture(_:)))
|
|
panRecognizer.cancelsTouchesInView = true
|
|
panRecognizer.delegate = self
|
|
self.view.addGestureRecognizer(panRecognizer)
|
|
}
|
|
|
|
func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) {
|
|
transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: self.effectiveHeaderHeight)))
|
|
|
|
let headerHeight = max(MediaNavigationAccessoryHeaderNode.minimizedHeight, min(MediaNavigationAccessoryHeaderNode.maximizedHeight, self.effectiveHeaderHeight))
|
|
transition.updateFrame(node: self.headerNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: headerHeight)))
|
|
self.headerNode.updateLayout(size: CGSize(width: size.width, height: headerHeight), transition: transition)
|
|
|
|
let itemListHeight = max(0.0, self.effectiveHeaderHeight - headerHeight)
|
|
transition.updateFrame(node: self.itemListNode, frame: CGRect(origin: CGPoint(x: 0.0, y: headerHeight), size: CGSize(width: size.width, height: itemListHeight)))
|
|
self.itemListNode.updateLayout(size: CGSize(width: size.width, height: itemListHeight), maximizedHeight: max(10.0, size.height - MediaNavigationAccessoryHeaderNode.maximizedHeight), transition: transition)
|
|
}
|
|
|
|
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
|
|
if let result = self.hitTest(touch.location(in: self.view), with: nil) {
|
|
if result.disablesInteractiveTransitionGestureRecognizer {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
@objc func panGesture(_ recognizer: UIPanGestureRecognizer) {
|
|
let middleHeight = MediaNavigationAccessoryHeaderNode.maximizedHeight + MediaNavigationAccessoryItemListNode.minimizedPanelHeight
|
|
switch recognizer.state {
|
|
case .began:
|
|
self.draggingHeaderHeight = self.currentHeaderHeight
|
|
case .changed:
|
|
if let _ = self.draggingHeaderHeight {
|
|
let translation = recognizer.translation(in: self.view).y
|
|
self.draggingHeaderHeight = max(MediaNavigationAccessoryHeaderNode.minimizedHeight, self.currentHeaderHeight + translation)
|
|
self.updateLayout(size: self.bounds.size, transition: .immediate)
|
|
}
|
|
case .ended:
|
|
if let draggingHeaderHeight = self.draggingHeaderHeight {
|
|
self.draggingHeaderHeight = nil
|
|
let velocity = recognizer.velocity(in: self.view).y
|
|
if abs(velocity) > 500.0 {
|
|
if draggingHeaderHeight <= middleHeight {
|
|
if velocity < 0.0 {
|
|
self.currentHeaderHeight = MediaNavigationAccessoryHeaderNode.minimizedHeight
|
|
} else {
|
|
self.currentHeaderHeight = middleHeight
|
|
}
|
|
} else {
|
|
if velocity < 0.0 {
|
|
self.currentHeaderHeight = middleHeight
|
|
} else {
|
|
self.currentHeaderHeight = self.bounds.size.height
|
|
}
|
|
}
|
|
} else {
|
|
if draggingHeaderHeight < MediaNavigationAccessoryHeaderNode.maximizedHeight * 2.0 / 3.0 {
|
|
self.currentHeaderHeight = MediaNavigationAccessoryHeaderNode.minimizedHeight
|
|
} else if draggingHeaderHeight <= middleHeight + 100.0 {
|
|
self.currentHeaderHeight = middleHeight
|
|
} else {
|
|
self.currentHeaderHeight = self.bounds.size.height
|
|
}
|
|
}
|
|
self.updateLayout(size: self.bounds.size, transition: .animated(duration: 0.3, curve: .spring))
|
|
}
|
|
case .cancelled:
|
|
self.draggingHeaderHeight = nil
|
|
self.updateLayout(size: self.bounds.size, transition: .animated(duration: 0.3, curve: .spring))
|
|
default:
|
|
break
|
|
}
|
|
}
|
|
|
|
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
|
if !self.headerNode.frame.contains(point) && !self.itemListNode.frame.contains(point) {
|
|
return nil
|
|
}
|
|
return super.hitTest(point, with: event)
|
|
}
|
|
}
|