Improve context panels appearance animation

This commit is contained in:
Ilya Laktyushin 2021-06-17 16:22:53 +03:00
parent bb28e0e4d3
commit 302cfa6390
9 changed files with 61 additions and 25 deletions

View File

@ -174,7 +174,10 @@ final class CommandChatInputContextPanelNode: ChatInputContextPanelNode {
if let topItemOffset = topItemOffset {
let position = strongSelf.listView.layer.position
strongSelf.listView.layer.animatePosition(from: CGPoint(x: position.x, y: position.y + (strongSelf.listView.bounds.size.height - topItemOffset)), to: position, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring)
strongSelf.listView.position = CGPoint(x: position.x, y: position.y + (strongSelf.listView.bounds.size.height - topItemOffset))
ContainedViewLayoutTransition.animated(duration: 0.3, curve: .spring).animateView {
strongSelf.listView.position = position
}
}
}
})

View File

@ -120,6 +120,7 @@ final class CommandMenuChatInputContextPanelNode: ChatInputContextPanelNode {
if sendImmediately {
interfaceInteraction.sendBotCommand(command.peer, "/" + command.command.text)
} else {
interfaceInteraction.updateShowCommands { _ in return false }
interfaceInteraction.updateTextInputStateAndMode { textInputState, inputMode in
var commandQueryRange: NSRange?
inner: for (range, type, _) in textInputStateContextQueryRangeAndType(textInputState) {
@ -128,7 +129,6 @@ final class CommandMenuChatInputContextPanelNode: ChatInputContextPanelNode {
break inner
}
}
if let range = commandQueryRange {
let inputText = NSMutableAttributedString(attributedString: textInputState.inputText)
@ -143,7 +143,6 @@ final class CommandMenuChatInputContextPanelNode: ChatInputContextPanelNode {
let selectionPosition = (inputText.string as NSString).length + 1
return (ChatTextInputState(inputText: inputText, selectionRange: selectionPosition ..< selectionPosition), inputMode)
}
return (textInputState, inputMode)
}
}
}
@ -193,7 +192,10 @@ final class CommandMenuChatInputContextPanelNode: ChatInputContextPanelNode {
if let topItemOffset = topItemOffset {
let position = strongSelf.listView.layer.position
strongSelf.listView.layer.animatePosition(from: CGPoint(x: position.x, y: position.y + (strongSelf.listView.bounds.size.height - topItemOffset)), to: position, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring)
strongSelf.listView.position = CGPoint(x: position.x, y: position.y + (strongSelf.listView.bounds.size.height - topItemOffset))
ContainedViewLayoutTransition.animated(duration: 0.3, curve: .spring).animateView {
strongSelf.listView.position = position
}
}
}
})

View File

@ -72,7 +72,10 @@ final class DisabledContextResultsChatInputContextPanelNode: ChatInputContextPan
func animateIn() {
let position = self.containerNode.layer.position
self.containerNode.layer.animatePosition(from: CGPoint(x: position.x, y: position.y + (self.containerNode.bounds.height)), to: position, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring)
self.containerNode.position = CGPoint(x: position.x, y: position.y + (self.containerNode.bounds.height))
ContainedViewLayoutTransition.animated(duration: 0.3, curve: .spring).animateView {
self.containerNode.position = position
}
}
override func animateOut(completion: @escaping () -> Void) {

View File

@ -190,7 +190,10 @@ final class HashtagChatInputContextPanelNode: ChatInputContextPanelNode {
if let topItemOffset = topItemOffset {
let position = strongSelf.listView.layer.position
strongSelf.listView.layer.animatePosition(from: CGPoint(x: position.x, y: position.y + (strongSelf.listView.bounds.size.height - topItemOffset)), to: position, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring)
strongSelf.listView.position = CGPoint(x: position.x, y: position.y + (strongSelf.listView.bounds.size.height - topItemOffset))
ContainedViewLayoutTransition.animated(duration: 0.3, curve: .spring).animateView {
strongSelf.listView.position = position
}
}
}
})

View File

@ -317,12 +317,17 @@ final class HorizontalListContextResultsChatInputContextPanelNode: ChatInputCont
self.listView.transaction(deleteIndices: transition.deletions, insertIndicesAndItems: transition.insertions, updateIndicesAndItems: transition.updates, options: options, updateSizeAndInsets: nil, updateOpaqueState: HorizontalListContextResultsOpaqueState(entryCount: transition.entryCount, hasMore: transition.hasMore), completion: { [weak self] _ in
if let strongSelf = self, firstTime {
let position = strongSelf.listView.position
strongSelf.listView.isHidden = false
strongSelf.listView.layer.animatePosition(from: CGPoint(x: position.x, y: position.y + strongSelf.listView.bounds.size.width), to: position, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring)
strongSelf.separatorNode.isHidden = false
let separatorPosition = strongSelf.separatorNode.layer.position
strongSelf.separatorNode.layer.animatePosition(from: CGPoint(x: separatorPosition.x, y: separatorPosition.y + strongSelf.listView.bounds.size.width), to: separatorPosition, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring)
strongSelf.listView.isHidden = false
strongSelf.separatorNode.isHidden = false
strongSelf.listView.position = CGPoint(x: position.x, y: position.y + strongSelf.listView.bounds.size.width)
strongSelf.separatorNode.position = CGPoint(x: separatorPosition.x, y: separatorPosition.y + strongSelf.listView.bounds.size.width)
ContainedViewLayoutTransition.animated(duration: 0.3, curve: .spring).animateView {
strongSelf.listView.position = position
strongSelf.separatorNode.position = separatorPosition
}
}
})
}

View File

@ -39,7 +39,7 @@ private final class InlineReactionSearchStickersNode: ASDisplayNode, UIScrollVie
var previewedStickerItem: StickerPackItem?
var updateBackgroundOffset: ((CGFloat, ContainedViewLayoutTransition) -> Void)?
var updateBackgroundOffset: ((CGFloat, Bool, ContainedViewLayoutTransition) -> Void)?
var sendSticker: ((FileMediaReference, ASDisplayNode, CGRect) -> Void)?
var getControllerInteraction: (() -> ChatControllerInteraction?)?
@ -175,13 +175,13 @@ private final class InlineReactionSearchStickersNode: ASDisplayNode, UIScrollVie
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if !self.ignoreScrolling {
self.updateVisibleItems(synchronous: false)
self.updateBackground(transition: .immediate)
self.updateBackground(animateIn: false, transition: .immediate)
}
}
private func updateBackground(transition: ContainedViewLayoutTransition) {
private func updateBackground(animateIn: Bool, transition: ContainedViewLayoutTransition) {
if let topInset = self.topInset {
self.updateBackgroundOffset?(max(0.0, -self.scrollNode.view.contentOffset.y + topInset), transition)
self.updateBackgroundOffset?(max(0.0, -self.scrollNode.view.contentOffset.y + topInset), animateIn, transition)
}
}
@ -228,7 +228,7 @@ private final class InlineReactionSearchStickersNode: ASDisplayNode, UIScrollVie
let currentBackgroundOffset = max(0.0, -self.scrollNode.view.contentOffset.y + topInset)
if abs(currentBackgroundOffset - previousBackgroundOffset) > .ulpOfOne {
transition.animateOffsetAdditive(node: self.scrollNode, offset: currentBackgroundOffset - previousBackgroundOffset)
self.updateBackground(transition: transition)
self.updateBackground(animateIn: false, transition: transition)
}
} else {
self.animateInOnLayout = true
@ -247,7 +247,7 @@ private final class InlineReactionSearchStickersNode: ASDisplayNode, UIScrollVie
self.validLayout = size
if self.animateInOnLayout {
self.updateBackgroundOffset?(size.height, .immediate)
self.updateBackgroundOffset?(size.height, false, .immediate)
}
var synchronous = false
@ -265,12 +265,18 @@ private final class InlineReactionSearchStickersNode: ASDisplayNode, UIScrollVie
var backgroundTransition = transition
var animateIn = false
if self.animateInOnLayout {
animateIn = true
self.animateInOnLayout = false
backgroundTransition = .animated(duration: 0.3, curve: .spring)
if let topInset = self.topInset {
let currentBackgroundOffset = max(0.0, -self.scrollNode.view.contentOffset.y + topInset)
backgroundTransition.animateOffsetAdditive(node: self.scrollNode, offset: currentBackgroundOffset - size.height)
let bounds = self.scrollNode.bounds
self.scrollNode.bounds = bounds.offsetBy(dx: 0.0, dy: currentBackgroundOffset - size.height)
backgroundTransition.animateView {
self.scrollNode.bounds = bounds
}
}
} else {
if let previousBackgroundOffset = previousBackgroundOffset, let topInset = self.topInset {
@ -281,7 +287,7 @@ private final class InlineReactionSearchStickersNode: ASDisplayNode, UIScrollVie
}
}
self.updateBackground(transition: backgroundTransition)
self.updateBackground(animateIn: animateIn, transition: backgroundTransition)
}
private func updateItemsLayout(width: CGFloat) {
@ -460,12 +466,17 @@ final class InlineReactionSearchPanel: ChatInputContextPanelNode {
return self?.controllerInteraction
}
self.stickersNode.updateBackgroundOffset = { [weak self] offset, transition in
self.stickersNode.updateBackgroundOffset = { [weak self] offset, animateIn, transition in
guard let strongSelf = self, let (_, _) = strongSelf.validLayout else {
return
}
transition.updateFrame(node: strongSelf.backgroundContainerNode, frame: CGRect(origin: CGPoint(x: 0.0, y: offset), size: CGSize()), beginWithCurrentState: false)
if animateIn {
transition.animateView {
strongSelf.backgroundContainerNode.frame = CGRect(origin: CGPoint(x: 0.0, y: offset), size: CGSize())
}
} else {
transition.updateFrame(node: strongSelf.backgroundContainerNode, frame: CGRect(origin: CGPoint(x: 0.0, y: offset), size: CGSize()), beginWithCurrentState: false)
}
let cornersTransitionDistance: CGFloat = 20.0
let cornersTransition: CGFloat = max(0.0, min(1.0, (cornersTransitionDistance - offset) / cornersTransitionDistance))
transition.updateSublayerTransformScaleAndOffset(node: strongSelf.backgroundTopLeftContainerNode, scale: 1.0, offset: CGPoint(x: -cornersTransition * backgroundDiameter, y: 0.0), beginWithCurrentState: true)

View File

@ -217,7 +217,10 @@ final class MentionChatInputContextPanelNode: ChatInputContextPanelNode {
if let topItemOffset = topItemOffset {
let position = strongSelf.listView.layer.position
strongSelf.listView.layer.animatePosition(from: CGPoint(x: position.x, y: position.y + (strongSelf.listView.bounds.size.height - topItemOffset)), to: position, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring)
strongSelf.listView.position = CGPoint(x: position.x, y: position.y + (strongSelf.listView.bounds.size.height - topItemOffset))
ContainedViewLayoutTransition.animated(duration: 0.3, curve: .spring).animateView {
strongSelf.listView.position = position
}
}
}
})

View File

@ -303,7 +303,10 @@ final class StickersChatInputContextPanelNode: ChatInputContextPanelNode {
if let topItemOffset = topItemOffset {
let position = strongSelf.listView.layer.position
strongSelf.listView.layer.animatePosition(from: CGPoint(x: position.x, y: position.y + (strongSelf.listView.bounds.size.height - topItemOffset)), to: position, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring)
strongSelf.listView.position = CGPoint(x: position.x, y: position.y + (strongSelf.listView.bounds.size.height - topItemOffset))
ContainedViewLayoutTransition.animated(duration: 0.3, curve: .spring).animateView {
strongSelf.listView.position = position
}
}
}
})

View File

@ -257,7 +257,10 @@ final class VerticalListContextResultsChatInputContextPanelNode: ChatInputContex
if let topItemOffset = topItemOffset {
let position = strongSelf.listView.layer.position
strongSelf.listView.layer.animatePosition(from: CGPoint(x: position.x, y: position.y + (strongSelf.listView.bounds.size.height - topItemOffset)), to: position, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring)
strongSelf.listView.position = CGPoint(x: position.x, y: position.y + (strongSelf.listView.bounds.size.height - topItemOffset))
ContainedViewLayoutTransition.animated(duration: 0.3, curve: .spring).animateView {
strongSelf.listView.position = position
}
}
strongSelf.listView.isHidden = false