mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-23 06:35:51 +00:00
Context UI animations
This commit is contained in:
@@ -125,7 +125,7 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
fileprivate var dismissedForCancel: (() -> Void)?
|
||||
private let getController: () -> ContextControllerProtocol?
|
||||
private weak var gesture: ContextGesture?
|
||||
private var displayTextSelectionTip: Bool
|
||||
private var tip: ContextController.Tip?
|
||||
|
||||
private var didSetItemsReady = false
|
||||
let itemsReady = Promise<Bool>()
|
||||
@@ -169,7 +169,7 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
|
||||
private let blurBackground: Bool
|
||||
|
||||
init(account: Account, controller: ContextController, presentationData: PresentationData, source: ContextContentSource, items: Signal<[ContextMenuItem], NoError>, reactionItems: [ReactionContextItem], beginDismiss: @escaping (ContextMenuActionResult) -> Void, recognizer: TapLongTapOrDoubleTapGestureRecognizer?, gesture: ContextGesture?, reactionSelected: @escaping (ReactionContextItem.Reaction) -> Void, beganAnimatingOut: @escaping () -> Void, attemptTransitionControllerIntoNavigation: @escaping () -> Void, displayTextSelectionTip: Bool) {
|
||||
init(account: Account, controller: ContextController, presentationData: PresentationData, source: ContextContentSource, items: Signal<[ContextMenuItem], NoError>, reactionItems: [ReactionContextItem], beginDismiss: @escaping (ContextMenuActionResult) -> Void, recognizer: TapLongTapOrDoubleTapGestureRecognizer?, gesture: ContextGesture?, reactionSelected: @escaping (ReactionContextItem.Reaction) -> Void, beganAnimatingOut: @escaping () -> Void, attemptTransitionControllerIntoNavigation: @escaping () -> Void, tip: ContextController.Tip?) {
|
||||
self.presentationData = presentationData
|
||||
self.source = source
|
||||
self.items = items
|
||||
@@ -178,7 +178,7 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
self.beganAnimatingOut = beganAnimatingOut
|
||||
self.attemptTransitionControllerIntoNavigation = attemptTransitionControllerIntoNavigation
|
||||
self.gesture = gesture
|
||||
self.displayTextSelectionTip = displayTextSelectionTip
|
||||
self.tip = tip
|
||||
|
||||
self.getController = { [weak controller] in
|
||||
return controller
|
||||
@@ -237,7 +237,7 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
beginDismiss(result)
|
||||
}, feedbackTap: {
|
||||
feedbackTap?()
|
||||
}, displayTextSelectionTip: self.displayTextSelectionTip, blurBackground: blurBackground)
|
||||
}, tip: self.tip, blurBackground: blurBackground)
|
||||
|
||||
if !reactionItems.isEmpty {
|
||||
let reactionContextNode = ReactionContextNode(account: account, theme: presentationData.theme, items: reactionItems)
|
||||
@@ -1199,24 +1199,25 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
self.currentActionsMinHeight = minHeight
|
||||
|
||||
let previousActionsContainerNode = self.actionsContainerNode
|
||||
let previousActionsContainerFrame = previousActionsContainerNode.view.convert(previousActionsContainerNode.bounds, to: self.view)
|
||||
self.actionsContainerNode = ContextActionsContainerNode(presentationData: self.presentationData, items: items, getController: { [weak self] in
|
||||
return self?.getController()
|
||||
}, actionSelected: { [weak self] result in
|
||||
self?.beginDismiss(result)
|
||||
}, feedbackTap: { [weak self] in
|
||||
self?.hapticFeedback.tap()
|
||||
}, displayTextSelectionTip: self.displayTextSelectionTip, blurBackground: self.blurBackground)
|
||||
}, tip: self.tip, blurBackground: self.blurBackground)
|
||||
self.scrollNode.insertSubnode(self.actionsContainerNode, aboveSubnode: previousActionsContainerNode)
|
||||
|
||||
if let layout = self.validLayout {
|
||||
self.updateLayout(layout: layout, transition: .animated(duration: 0.3, curve: .spring), previousActionsContainerNode: previousActionsContainerNode, previousActionsTransition: previousActionsTransition)
|
||||
self.updateLayout(layout: layout, transition: .animated(duration: 0.3, curve: .spring), previousActionsContainerNode: previousActionsContainerNode, previousActionsContainerFrame: previousActionsContainerFrame, previousActionsTransition: previousActionsTransition)
|
||||
} else {
|
||||
previousActionsContainerNode.removeFromSupernode()
|
||||
}
|
||||
|
||||
if !self.didSetItemsReady {
|
||||
self.didSetItemsReady = true
|
||||
self.displayTextSelectionTip = false
|
||||
self.tip = nil
|
||||
self.itemsReady.set(.single(true))
|
||||
}
|
||||
}
|
||||
@@ -1228,11 +1229,11 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
self.actionsContainerNode.updateTheme(presentationData: presentationData)
|
||||
|
||||
if let validLayout = self.validLayout {
|
||||
self.updateLayout(layout: validLayout, transition: .immediate, previousActionsContainerNode: nil)
|
||||
self.updateLayout(layout: validLayout, transition: .immediate, previousActionsContainerNode: nil, previousActionsContainerFrame: nil)
|
||||
}
|
||||
}
|
||||
|
||||
func updateLayout(layout: ContainerViewLayout, transition: ContainedViewLayoutTransition, previousActionsContainerNode: ContextActionsContainerNode?, previousActionsTransition: ContextController.PreviousActionsTransition = .scale) {
|
||||
func updateLayout(layout: ContainerViewLayout, transition: ContainedViewLayoutTransition, previousActionsContainerNode: ContextActionsContainerNode?, previousActionsContainerFrame: CGRect? = nil, previousActionsTransition: ContextController.PreviousActionsTransition = .scale) {
|
||||
if self.isAnimatingOut {
|
||||
return
|
||||
}
|
||||
@@ -1475,12 +1476,18 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
actionsContainerTransition.updateFrame(node: self.actionsContainerNode, frame: originalActionsFrame.offsetBy(dx: 0.0, dy: -overflowOffset))
|
||||
|
||||
if isInitialLayout {
|
||||
let previousContentOffset = self.scrollNode.view.contentOffset.y
|
||||
if !keepInPlace {
|
||||
self.scrollNode.view.contentOffset = CGPoint(x: 0.0, y: -overflowOffset)
|
||||
}
|
||||
let currentContainerFrame = self.view.convert(self.contentContainerNode.frame, from: self.scrollNode.view)
|
||||
var offset: CGFloat = 0.0
|
||||
offset -= previousContentOffset - self.scrollNode.view.contentOffset.y
|
||||
//offset += previousContainerFrame.minY - currentContainerFrame.minY
|
||||
transition.animatePositionAdditive(node: self.contentContainerNode, offset: CGPoint(x: 0.0, y: offset))
|
||||
if overflowOffset < 0.0 {
|
||||
transition.animateOffsetAdditive(node: self.scrollNode, offset: currentContainerFrame.minY - previousContainerFrame.minY)
|
||||
let _ = currentContainerFrame
|
||||
let _ = previousContainerFrame
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1650,6 +1657,10 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
})
|
||||
self.actionsContainerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
} else {
|
||||
if let previousActionsContainerFrame = previousActionsContainerFrame {
|
||||
previousActionsContainerNode.frame = self.view.convert(previousActionsContainerFrame, to: self.actionsContainerNode.view.superview!)
|
||||
}
|
||||
|
||||
switch previousActionsTransition {
|
||||
case .scale:
|
||||
transition.updateTransformScale(node: previousActionsContainerNode, scale: 0.1)
|
||||
@@ -1660,33 +1671,26 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
transition.animateTransformScale(node: self.actionsContainerNode, from: 0.1)
|
||||
self.actionsContainerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
case let .slide(forward):
|
||||
if case .compact = layout.metrics.widthClass {
|
||||
if forward {
|
||||
transition.updatePosition(node: previousActionsContainerNode, position: CGPoint(x: -previousActionsContainerNode.bounds.width / 2.0, y: previousActionsContainerNode.position.y), completion: { [weak previousActionsContainerNode] _ in
|
||||
previousActionsContainerNode?.removeFromSupernode()
|
||||
})
|
||||
transition.animatePositionAdditive(node: self.actionsContainerNode, offset: CGPoint(x: layout.size.width + self.actionsContainerNode.bounds.width / 2.0 - self.actionsContainerNode.position.x, y: 0.0))
|
||||
} else {
|
||||
transition.updatePosition(node: previousActionsContainerNode, position: CGPoint(x: layout.size.width + previousActionsContainerNode.bounds.width / 2.0, y: previousActionsContainerNode.position.y), completion: { [weak previousActionsContainerNode] _ in
|
||||
previousActionsContainerNode?.removeFromSupernode()
|
||||
})
|
||||
transition.animatePositionAdditive(node: self.actionsContainerNode, offset: CGPoint(x: -self.actionsContainerNode.bounds.width / 2.0 - self.actionsContainerNode.position.x, y: 0.0))
|
||||
}
|
||||
let deltaY = self.actionsContainerNode.frame.minY - previousActionsContainerNode.frame.minY
|
||||
var previousNodePosition = previousActionsContainerNode.position.offsetBy(dx: 0.0, dy: deltaY)
|
||||
let additionalHorizontalOffset: CGFloat = 20.0
|
||||
let currentNodeOffset: CGFloat
|
||||
if forward {
|
||||
previousNodePosition = previousNodePosition.offsetBy(dx: -previousActionsContainerNode.frame.width / 2.0 - additionalHorizontalOffset, dy: -previousActionsContainerNode.frame.height / 2.0)
|
||||
currentNodeOffset = self.actionsContainerNode.bounds.width / 2.0 + additionalHorizontalOffset
|
||||
} else {
|
||||
let offset: CGFloat
|
||||
if forward {
|
||||
offset = previousActionsContainerNode.bounds.width
|
||||
} else {
|
||||
offset = -previousActionsContainerNode.bounds.width
|
||||
}
|
||||
transition.updatePosition(node: previousActionsContainerNode, position: previousActionsContainerNode.position.offsetBy(dx: -offset, dy: 0.0))
|
||||
previousActionsContainerNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak previousActionsContainerNode] _ in
|
||||
previousActionsContainerNode?.removeFromSupernode()
|
||||
})
|
||||
|
||||
transition.animatePositionAdditive(node: self.actionsContainerNode, offset: CGPoint(x: offset, y: 0.0))
|
||||
self.actionsContainerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
previousNodePosition = previousNodePosition.offsetBy(dx: previousActionsContainerNode.frame.width / 2.0 + additionalHorizontalOffset, dy: -previousActionsContainerNode.frame.height / 2.0)
|
||||
currentNodeOffset = -self.actionsContainerNode.bounds.width / 2.0 - additionalHorizontalOffset
|
||||
}
|
||||
transition.updatePosition(node: previousActionsContainerNode, position: previousNodePosition)
|
||||
transition.updateTransformScale(node: previousActionsContainerNode, scale: 0.01)
|
||||
previousActionsContainerNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak previousActionsContainerNode] _ in
|
||||
previousActionsContainerNode?.removeFromSupernode()
|
||||
})
|
||||
|
||||
transition.animatePositionAdditive(node: self.actionsContainerNode, offset: CGPoint(x: currentNodeOffset, y: -deltaY - self.actionsContainerNode.bounds.height / 2.0))
|
||||
transition.animateTransformScale(node: self.actionsContainerNode, from: 0.01)
|
||||
self.actionsContainerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -1868,6 +1872,11 @@ public final class ContextController: ViewController, StandalonePresentableContr
|
||||
case slide(forward: Bool)
|
||||
}
|
||||
|
||||
public enum Tip {
|
||||
case textSelection
|
||||
case messageViewsPrivacy
|
||||
}
|
||||
|
||||
private let account: Account
|
||||
private var presentationData: PresentationData
|
||||
private let source: ContextContentSource
|
||||
@@ -1881,7 +1890,7 @@ public final class ContextController: ViewController, StandalonePresentableContr
|
||||
|
||||
private weak var recognizer: TapLongTapOrDoubleTapGestureRecognizer?
|
||||
private weak var gesture: ContextGesture?
|
||||
private let displayTextSelectionTip: Bool
|
||||
private let tip: Tip?
|
||||
|
||||
private var animatedDidAppear = false
|
||||
private var wasDismissed = false
|
||||
@@ -1903,7 +1912,7 @@ public final class ContextController: ViewController, StandalonePresentableContr
|
||||
|
||||
private var shouldBeDismissedDisposable: Disposable?
|
||||
|
||||
public init(account: Account, presentationData: PresentationData, source: ContextContentSource, items: Signal<[ContextMenuItem], NoError>, reactionItems: [ReactionContextItem], recognizer: TapLongTapOrDoubleTapGestureRecognizer? = nil, gesture: ContextGesture? = nil, displayTextSelectionTip: Bool = false) {
|
||||
public init(account: Account, presentationData: PresentationData, source: ContextContentSource, items: Signal<[ContextMenuItem], NoError>, reactionItems: [ReactionContextItem], recognizer: TapLongTapOrDoubleTapGestureRecognizer? = nil, gesture: ContextGesture? = nil, tip: Tip? = nil) {
|
||||
self.account = account
|
||||
self.presentationData = presentationData
|
||||
self.source = source
|
||||
@@ -1911,7 +1920,7 @@ public final class ContextController: ViewController, StandalonePresentableContr
|
||||
self.reactionItems = reactionItems
|
||||
self.recognizer = recognizer
|
||||
self.gesture = gesture
|
||||
self.displayTextSelectionTip = displayTextSelectionTip
|
||||
self.tip = tip
|
||||
|
||||
super.init(navigationBarPresentationData: nil)
|
||||
|
||||
@@ -1982,7 +1991,7 @@ public final class ContextController: ViewController, StandalonePresentableContr
|
||||
default:
|
||||
break
|
||||
}
|
||||
}, displayTextSelectionTip: self.displayTextSelectionTip)
|
||||
}, tip: self.tip)
|
||||
self.controllerNode.dismissedForCancel = self.dismissedForCancel
|
||||
self.displayNodeDidLoad()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user