From 6ef30496e13e64f9053da36bda94a302d678a33f Mon Sep 17 00:00:00 2001 From: Ali <> Date: Fri, 23 Apr 2021 16:30:29 +0400 Subject: [PATCH] Message animation improvements --- .../Display/Source/CAAnimationUtils.swift | 6 +- submodules/Display/Source/ListView.swift | 34 +++++--- .../TelegramUI/Sources/ChatController.swift | 9 +++ .../Sources/ChatControllerNode.swift | 21 ++++- .../Sources/ChatHistoryListNode.swift | 2 +- .../ChatMessageAnimatedStickerItemNode.swift | 2 +- .../Sources/ChatMessageBubbleBackdrop.swift | 2 +- .../Sources/ChatMessageBubbleItemNode.swift | 20 ++--- .../ChatMessageDateAndStatusNode.swift | 2 +- .../Sources/ChatMessageReplyInfoNode.swift | 18 ++--- .../Sources/ChatMessageStickerItemNode.swift | 2 +- .../Sources/ChatMessageTransitionNode.swift | 81 ++++++++++++++++--- .../Sources/ChatTextInputPanelNode.swift | 56 +++++++++---- .../PeerInfoGroupsInCommonPaneNode.swift | 2 +- .../PeerInfo/Panes/PeerInfoListPaneNode.swift | 4 +- .../PeerInfo/Panes/PeerInfoMembersPane.swift | 2 +- 16 files changed, 193 insertions(+), 70 deletions(-) diff --git a/submodules/Display/Source/CAAnimationUtils.swift b/submodules/Display/Source/CAAnimationUtils.swift index 4aecee5b73..060f9fb134 100644 --- a/submodules/Display/Source/CAAnimationUtils.swift +++ b/submodules/Display/Source/CAAnimationUtils.swift @@ -72,7 +72,7 @@ public extension CALayer { animation.isAdditive = additive if !delay.isZero { - animation.beginTime = CACurrentMediaTime() + delay * UIView.animationDurationFactor() + animation.beginTime = self.convertTime(CACurrentMediaTime(), from: nil) + delay * UIView.animationDurationFactor() animation.fillMode = .both } @@ -102,7 +102,7 @@ public extension CALayer { } if !delay.isZero { - animation.beginTime = CACurrentMediaTime() + delay * UIView.animationDurationFactor() + animation.beginTime = self.convertTime(CACurrentMediaTime(), from: nil) + delay * UIView.animationDurationFactor() animation.fillMode = .both } @@ -278,7 +278,7 @@ public extension CALayer { self.animateKeyframes(values: values.map { NSValue(cgPoint: $0) }, duration: duration, keyPath: "position") } - func animateFrame(from: CGRect, to: CGRect, duration: Double, delay: Double = 0.0, timingFunction: String, mediaTimingFunction: CAMediaTimingFunction? = nil, removeOnCompletion: Bool = true, additive: Bool = false, force: Bool = false, completion: ((Bool) -> Void)? = nil) { + func animateFrame(from: CGRect, to: CGRect, duration: Double, delay: Double = 0.0, timingFunction: String = CAMediaTimingFunctionName.easeInEaseOut.rawValue, mediaTimingFunction: CAMediaTimingFunction? = nil, removeOnCompletion: Bool = true, additive: Bool = false, force: Bool = false, completion: ((Bool) -> Void)? = nil) { if from == to && !force { if let completion = completion { completion(true) diff --git a/submodules/Display/Source/ListView.swift b/submodules/Display/Source/ListView.swift index d188c563c7..9337c67555 100644 --- a/submodules/Display/Source/ListView.swift +++ b/submodules/Display/Source/ListView.swift @@ -214,6 +214,7 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture } public final var didScrollWithOffset: ((CGFloat, ContainedViewLayoutTransition, ListViewItemNode?) -> Void)? + public final var addContentOffset: ((CGFloat, ListViewItemNode?) -> Void)? private var topItemOverscrollBackground: ListViewOverscrollBackgroundNode? private var bottomItemOverscrollBackground: ASDisplayNode? @@ -2129,6 +2130,7 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture offsetHeight = 0.0 nextNode.updateFrame(nextNode.frame.offsetBy(dx: 0.0, dy: nextHeight), within: self.visibleSize) + self.didScrollWithOffset?(nextHeight, .immediate, nextNode) nextNode.apparentHeight = 0.0 @@ -2238,6 +2240,7 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture var frame = self.itemNodes[i].frame frame.origin.y -= offsetHeight self.itemNodes[i].updateFrame(frame, within: self.visibleSize) + //self.didScrollWithOffset?(offsetHeight, .immediate, self.itemNodes[i]) if let accessoryItemNode = self.itemNodes[i].accessoryItemNode { self.itemNodes[i].layoutAccessoryItemNode(accessoryItemNode, leftInset: listInsets.left, rightInset: listInsets.right) } @@ -2249,6 +2252,7 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture var frame = self.itemNodes[i].frame frame.origin.y += offsetHeight self.itemNodes[i].updateFrame(frame, within: self.visibleSize) + //self.didScrollWithOffset?(-offsetHeight, .immediate, self.itemNodes[i]) if let accessoryItemNode = self.itemNodes[i].accessoryItemNode { self.itemNodes[i].layoutAccessoryItemNode(accessoryItemNode, leftInset: listInsets.left, rightInset: listInsets.right) } @@ -2674,7 +2678,7 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture } } - self.didScrollWithOffset?(-offset, scrollToItemTransition, nil) + //self.didScrollWithOffset?(-offset, scrollToItemTransition, nil) } for itemNode in self.itemNodes { @@ -2761,8 +2765,10 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture updateSizeAndInsetsTransition = .animated(duration: duration, curve: .custom(cp1x, cp1y, cp2x, cp2y)) } } - - self.didScrollWithOffset?(-offsetFix, updateSizeAndInsetsTransition, nil) + + if !offsetFix.isZero { + //self.didScrollWithOffset?(-offsetFix, updateSizeAndInsetsTransition, nil) + } for itemNode in self.itemNodes { itemNode.updateFrame(itemNode.frame.offsetBy(dx: 0.0, dy: offsetFix), within: self.visibleSize) @@ -2773,7 +2779,7 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture if !snappedTopInset.isZero && (previousVisibleSize.height.isZero || previousApparentFrames.isEmpty) { offsetFix += snappedTopInset - self.didScrollWithOffset?(-offsetFix, .immediate, nil) + //self.didScrollWithOffset?(-snappedTopInset, .immediate, nil) for itemNode in self.itemNodes { itemNode.updateFrame(itemNode.frame.offsetBy(dx: 0.0, dy: snappedTopInset), within: self.visibleSize) @@ -2847,10 +2853,12 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture self?.updateItemNodesVisibilities(onlyPositive: false) } self.layer.add(animation, forKey: nil) - for itemNode in self.itemNodes { - itemNode.applyAbsoluteOffset(value: CGPoint(x: 0.0, y: -completeOffset), animationCurve: animationCurve, duration: animationDuration) + if !completeOffset.isZero { + for itemNode in self.itemNodes { + itemNode.applyAbsoluteOffset(value: CGPoint(x: 0.0, y: -completeOffset), animationCurve: animationCurve, duration: animationDuration) + } + self.didScrollWithOffset?(-completeOffset, ContainedViewLayoutTransition.animated(duration: animationDuration, curve: animationCurve), nil) } - //self.didScrollWithOffset?(-completeOffset, ContainedViewLayoutTransition.animated(duration: animationDuration, curve: animationCurve), nil) } } else { self.visibleSize = updateSizeAndInsets.size @@ -2888,8 +2896,12 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture springAnimation.isAdditive = true self.layer.add(springAnimation, forKey: nil) - for itemNode in self.itemNodes { - itemNode.applyAbsoluteOffset(value: CGPoint(x: 0.0, y: -completeOffset), animationCurve: .spring, duration: duration) + + if !completeOffset.isZero { + for itemNode in self.itemNodes { + itemNode.applyAbsoluteOffset(value: CGPoint(x: 0.0, y: -completeOffset), animationCurve: .spring, duration: duration) + } + self.didScrollWithOffset?(-completeOffset, .animated(duration: duration, curve: .spring), nil) } } else { if let snapshotView = snapshotView { @@ -3026,7 +3038,7 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture self.updateItemHeaders(leftInset: listInsets.left, rightInset: listInsets.right, transition: headerNodesTransition, animateInsertion: animated || !requestItemInsertionAnimationsIndices.isEmpty) if let offset = offset, !offset.isZero { - self.didScrollWithOffset?(-offset, headerNodesTransition.0, nil) + //self.didScrollWithOffset?(-offset, headerNodesTransition.0, nil) let lowestNodeToInsertBelow = self.lowestNodeToInsertBelow() for itemNode in temporaryPreviousNodes { itemNode.updateFrame(itemNode.frame.offsetBy(dx: 0.0, dy: offset), within: self.visibleSize) @@ -3173,7 +3185,7 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture for itemNode in temporaryPreviousNodes { itemNode.applyAbsoluteOffset(value: CGPoint(x: 0.0, y: -offset), animationCurve: animationCurve, duration: animationDuration) } - self.didScrollWithOffset?(offset, .animated(duration: animationDuration, curve: animationCurve), nil) + self.didScrollWithOffset?(-offset, .animated(duration: animationDuration, curve: animationCurve), nil) if let verticalScrollIndicator = self.verticalScrollIndicator { verticalScrollIndicator.layer.add(reverseAnimation, forKey: nil) } diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index f56d8216f1..c3f0a7eea9 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -4087,6 +4087,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G guard let strongSelf = self else { return } + + //print("didScrollWithOffset offset: \(offset), itemNode: \(String(describing: itemNode))") if offset > 0.0 { if var scrolledToMessageIdValue = strongSelf.scrolledToMessageIdValue { @@ -4109,6 +4111,13 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G strongSelf.chatDisplayNode.messageTransitionNode.addExternalOffset(offset: offset, transition: transition, itemNode: itemNode) } + + self.chatDisplayNode.historyNode.addContentOffset = { [weak self] offset, itemNode in + guard let strongSelf = self else { + return + } + strongSelf.chatDisplayNode.messageTransitionNode.addContentOffset(offset: offset, itemNode: itemNode) + } if case .pinnedMessages = self.presentationInterfaceState.subject { self.chatDisplayNode.historyNode.setLoadStateUpdated({ [weak self] state, _ in diff --git a/submodules/TelegramUI/Sources/ChatControllerNode.swift b/submodules/TelegramUI/Sources/ChatControllerNode.swift index 6c3c6b2841..53800e9a45 100644 --- a/submodules/TelegramUI/Sources/ChatControllerNode.swift +++ b/submodules/TelegramUI/Sources/ChatControllerNode.swift @@ -505,9 +505,20 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { self.navigationBarSeparatorNode = ASDisplayNode() self.navigationBarSeparatorNode.backgroundColor = chatPresentationInterfaceState.theme.rootController.navigationBar.separatorColor - self.messageTransitionNode = ChatMessageTransitionNode(listNode: self.historyNode) + var getContentAreaInScreenSpaceImpl: (() -> CGRect)? + self.messageTransitionNode = ChatMessageTransitionNode(listNode: self.historyNode, getContentAreaInScreenSpace: { + return getContentAreaInScreenSpaceImpl?() ?? CGRect() + }) super.init() + + getContentAreaInScreenSpaceImpl = { [weak self] in + guard let strongSelf = self else { + return CGRect() + } + + return strongSelf.view.convert(strongSelf.frameForVisibleArea(), to: nil) + } self.controller?.presentationContext.topLevelSubview = { [weak self] in guard let strongSelf = self else { @@ -787,6 +798,8 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { if !self.historyNode.frame.isEmpty { previousListBottomInset = self.historyNode.insets.top } + + self.messageTransitionNode.frame = CGRect(origin: CGPoint(), size: layout.size) self.scheduledLayoutTransitionRequest = nil if case .overlay = self.chatPresentationInterfaceState.mode { @@ -1880,9 +1893,9 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { self.updatePlainInputSeparator(transition: transition) - let listBottomInset = self.historyNode.insets.bottom + let listBottomInset = self.historyNode.insets.top if let previousListBottomInset = previousListBottomInset, listBottomInset != previousListBottomInset { - self.historyNode.didScrollWithOffset?(listBottomInset - previousListBottomInset, transition, nil) + //self.historyNode.didScrollWithOffset?(listBottomInset - previousListBottomInset, transition, nil) } self.derivedLayoutState = ChatControllerNodeDerivedLayoutState(inputContextPanelsFrame: inputContextPanelsFrame, inputContextPanelsOverMainPanelFrame: inputContextPanelsOverMainPanelFrame, inputNodeHeight: inputNodeHeightAndOverflow?.0, inputNodeAdditionalHeight: inputNodeHeightAndOverflow?.1, upperInputPositionBound: inputNodeHeightAndOverflow?.0 != nil ? self.upperInputPositionBound : nil) @@ -2900,7 +2913,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { var shouldAnimateMessageTransition: Bool { switch self.historyNode.visibleContentOffset() { - case .known(0.0): + case let .known(value) where value < 20.0: return true default: return false diff --git a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift index df6b000234..4d138fabc3 100644 --- a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift +++ b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift @@ -1588,7 +1588,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { public func scrollToEndOfHistory() { self.beganDragging?() switch self.visibleContentOffset() { - case .known(0.0): + case let .known(value) where value <= CGFloat.ulpOfOne: break default: let locationInput = ChatHistoryLocationInput(content: .Scroll(index: .upperBound, anchorIndex: .upperBound, sourceIndex: .lowerBound, scrollPosition: .top(0.0), animated: true, highlight: false), id: self.takeNextHistoryLocationId()) diff --git a/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift index e89fe77d1b..c5eaeff6f2 100644 --- a/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift @@ -1885,7 +1885,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { if let replyInfoNode = self.replyInfoNode { let localRect = self.contextSourceNode.contentNode.view.convert(sourceReplyPanel.relativeSourceRect, to: replyInfoNode.view) - let offset = replyInfoNode.animateFromInputPanel(sourceReplyPanel: sourceReplyPanel, localRect: localRect, transition: transition) + let offset = replyInfoNode.animateFromInputPanel(sourceReplyPanel: sourceReplyPanel, localRect: localRect, horizontalTransition: transition, verticalTransition: transition) if let replyBackgroundNode = self.replyBackgroundNode { transition.animatePositionAdditive(node: replyBackgroundNode, offset: offset) replyBackgroundNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1) diff --git a/submodules/TelegramUI/Sources/ChatMessageBubbleBackdrop.swift b/submodules/TelegramUI/Sources/ChatMessageBubbleBackdrop.swift index 842574b8b2..d173946f6a 100644 --- a/submodules/TelegramUI/Sources/ChatMessageBubbleBackdrop.swift +++ b/submodules/TelegramUI/Sources/ChatMessageBubbleBackdrop.swift @@ -165,7 +165,7 @@ final class ChatMessageBubbleBackdrop: ASDisplayNode { func animateFrom(sourceView: UIView, mediaBox: MediaBox, transition: ContainedViewLayoutTransition) { if transition.isAnimated { let previousFrame = self.frame - self.updateFrame(CGRect(origin: previousFrame.origin, size: sourceView.frame.size), transition: .immediate) + self.updateFrame(CGRect(origin: CGPoint(x: previousFrame.minX, y: sourceView.frame.minY), size: sourceView.frame.size), transition: .immediate) self.updateFrame(previousFrame, transition: transition) self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1) diff --git a/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift index c310d8f781..b627e0a544 100644 --- a/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift @@ -654,32 +654,34 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode } } - func animateContentFromTextInputField(textInput: ChatMessageTransitionNode.Source.TextInput, transition: ContainedViewLayoutTransition) { + func animateContentFromTextInputField(textInput: ChatMessageTransitionNode.Source.TextInput, horizontalTransition: ContainedViewLayoutTransition, verticalTransition: ContainedViewLayoutTransition) { guard let item = self.item else { return } let widthDifference = self.backgroundNode.frame.width - textInput.backgroundView.frame.width + let heightDifference = self.backgroundNode.frame.height - textInput.backgroundView.frame.height - textInput.backgroundView.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: textInput.backgroundView.bounds.size) + horizontalTransition.animateFrame(node: self.clippingNode, from: CGRect(origin: CGPoint(x: self.clippingNode.frame.minX, y: textInput.backgroundView.frame.minY), size: textInput.backgroundView.frame.size)) + horizontalTransition.animateOffsetAdditive(layer: self.clippingNode.layer, offset: textInput.backgroundView.frame.minY - self.clippingNode.frame.minY) - transition.animateFrame(node: self.clippingNode, from: CGRect(origin: self.clippingNode.frame.origin, size: textInput.backgroundView.frame.size)) - - self.backgroundWallpaperNode.animateFrom(sourceView: textInput.backgroundView, mediaBox: item.context.account.postbox.mediaBox, transition: transition) - self.backgroundNode.animateFrom(sourceView: textInput.backgroundView, transition: transition) + self.backgroundWallpaperNode.animateFrom(sourceView: textInput.backgroundView, mediaBox: item.context.account.postbox.mediaBox, transition: horizontalTransition) + self.backgroundNode.animateFrom(sourceView: textInput.backgroundView, transition: horizontalTransition) for contentNode in self.contentNodes { if let contentNode = contentNode as? ChatMessageTextBubbleContentNode { let localSourceContentFrame = self.mainContextSourceNode.contentNode.view.convert(textInput.contentView.frame.offsetBy(dx: self.mainContextSourceNode.contentRect.minX, dy: self.mainContextSourceNode.contentRect.minY), to: contentNode.view) textInput.contentView.frame = localSourceContentFrame - contentNode.animateFrom(sourceView: textInput.contentView, widthDifference: widthDifference, transition: transition) + contentNode.animateFrom(sourceView: textInput.contentView, widthDifference: widthDifference, transition: horizontalTransition) + } else if let contentNode = contentNode as? ChatMessageWebpageBubbleContentNode { + horizontalTransition.animatePositionAdditive(node: contentNode, offset: CGPoint(x: 0.0, y: heightDifference)) } } } - func animateReplyPanel(sourceReplyPanel: ChatMessageTransitionNode.ReplyPanel, transition: ContainedViewLayoutTransition) { + func animateReplyPanel(sourceReplyPanel: ChatMessageTransitionNode.ReplyPanel, horizontalTransition: ContainedViewLayoutTransition, verticalTransition: ContainedViewLayoutTransition) { if let replyInfoNode = self.replyInfoNode { let localRect = self.mainContextSourceNode.contentNode.view.convert(sourceReplyPanel.relativeSourceRect, to: replyInfoNode.view) - let _ = replyInfoNode.animateFromInputPanel(sourceReplyPanel: sourceReplyPanel, localRect: localRect, transition: transition) + let _ = replyInfoNode.animateFromInputPanel(sourceReplyPanel: sourceReplyPanel, localRect: localRect, horizontalTransition: horizontalTransition, verticalTransition: verticalTransition) } } diff --git a/submodules/TelegramUI/Sources/ChatMessageDateAndStatusNode.swift b/submodules/TelegramUI/Sources/ChatMessageDateAndStatusNode.swift index 1328a749ad..a550cf4a3d 100644 --- a/submodules/TelegramUI/Sources/ChatMessageDateAndStatusNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageDateAndStatusNode.swift @@ -433,7 +433,7 @@ class ChatMessageDateAndStatusNode: ASDisplayNode { if let outgoingStatus = outgoingStatus { switch outgoingStatus { case .Sending: - statusWidth = 13.0 + statusWidth = floor(floor(presentationData.fontSize.baseDisplaySize * 13.0 / 17.0)) if checkReadNode == nil { checkReadNode = ASImageNode() diff --git a/submodules/TelegramUI/Sources/ChatMessageReplyInfoNode.swift b/submodules/TelegramUI/Sources/ChatMessageReplyInfoNode.swift index bdea98855b..8aded002eb 100644 --- a/submodules/TelegramUI/Sources/ChatMessageReplyInfoNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageReplyInfoNode.swift @@ -230,14 +230,14 @@ class ChatMessageReplyInfoNode: ASDisplayNode { } } - func animateFromInputPanel(sourceReplyPanel: ChatMessageTransitionNode.ReplyPanel, localRect: CGRect, transition: ContainedViewLayoutTransition) -> CGPoint { + func animateFromInputPanel(sourceReplyPanel: ChatMessageTransitionNode.ReplyPanel, localRect: CGRect, horizontalTransition: ContainedViewLayoutTransition, verticalTransition: ContainedViewLayoutTransition) -> CGPoint { if let titleNode = self.titleNode { let offset = CGPoint( x: localRect.minX + sourceReplyPanel.titleNode.frame.minX - titleNode.frame.minX, y: localRect.minY + sourceReplyPanel.titleNode.frame.midY - titleNode.frame.midY ) - transition.animatePositionAdditive(node: titleNode, offset: offset) + horizontalTransition.animatePositionAdditive(node: titleNode, offset: offset) self.addSubnode(sourceReplyPanel.titleNode) @@ -247,7 +247,7 @@ class ChatMessageReplyInfoNode: ASDisplayNode { }) sourceReplyPanel.titleNode.frame = sourceReplyPanel.titleNode.frame.offsetBy(dx: localRect.minX - offset.x, dy: localRect.minY - offset.y) - transition.animatePositionAdditive(node: sourceReplyPanel.titleNode, offset: CGPoint(x: offset.x, y: offset.y), removeOnCompletion: false) + horizontalTransition.animatePositionAdditive(node: sourceReplyPanel.titleNode, offset: CGPoint(x: offset.x, y: offset.y), removeOnCompletion: false) } if let textNode = self.textNode { @@ -256,7 +256,7 @@ class ChatMessageReplyInfoNode: ASDisplayNode { y: localRect.minY + sourceReplyPanel.textNode.frame.midY - textNode.frame.midY ) - transition.animatePositionAdditive(node: textNode, offset: offset) + horizontalTransition.animatePositionAdditive(node: textNode, offset: offset) self.addSubnode(sourceReplyPanel.textNode) @@ -266,7 +266,7 @@ class ChatMessageReplyInfoNode: ASDisplayNode { }) sourceReplyPanel.textNode.frame = sourceReplyPanel.textNode.frame.offsetBy(dx: localRect.minX - offset.x, dy: localRect.minY - offset.y) - transition.animatePositionAdditive(node: sourceReplyPanel.textNode, offset: CGPoint(x: offset.x, y: offset.y), removeOnCompletion: false) + horizontalTransition.animatePositionAdditive(node: sourceReplyPanel.textNode, offset: CGPoint(x: offset.x, y: offset.y), removeOnCompletion: false) } if let imageNode = self.imageNode { @@ -275,7 +275,7 @@ class ChatMessageReplyInfoNode: ASDisplayNode { y: localRect.minY + sourceReplyPanel.imageNode.frame.midY - imageNode.frame.midY ) - transition.animatePositionAdditive(node: imageNode, offset: offset) + horizontalTransition.animatePositionAdditive(node: imageNode, offset: offset) self.addSubnode(sourceReplyPanel.imageNode) @@ -285,7 +285,7 @@ class ChatMessageReplyInfoNode: ASDisplayNode { }) sourceReplyPanel.imageNode.frame = sourceReplyPanel.imageNode.frame.offsetBy(dx: localRect.minX - offset.x, dy: localRect.minY - offset.y) - transition.animatePositionAdditive(node: sourceReplyPanel.imageNode, offset: CGPoint(x: offset.x, y: offset.y), removeOnCompletion: false) + horizontalTransition.animatePositionAdditive(node: sourceReplyPanel.imageNode, offset: CGPoint(x: offset.x, y: offset.y), removeOnCompletion: false) } do { @@ -296,7 +296,7 @@ class ChatMessageReplyInfoNode: ASDisplayNode { y: localRect.minY + sourceReplyPanel.lineNode.frame.minY - lineNode.frame.minY ) - transition.animatePositionAdditive(node: lineNode, offset: offset) + horizontalTransition.animatePositionAdditive(node: lineNode, offset: offset) self.addSubnode(sourceReplyPanel.lineNode) @@ -306,7 +306,7 @@ class ChatMessageReplyInfoNode: ASDisplayNode { }) sourceReplyPanel.lineNode.frame = sourceReplyPanel.lineNode.frame.offsetBy(dx: localRect.minX - offset.x, dy: localRect.minY - offset.y) - transition.animatePositionAdditive(node: sourceReplyPanel.lineNode, offset: CGPoint(x: offset.x, y: offset.y), removeOnCompletion: false) + horizontalTransition.animatePositionAdditive(node: sourceReplyPanel.lineNode, offset: CGPoint(x: offset.x, y: offset.y), removeOnCompletion: false) return offset } diff --git a/submodules/TelegramUI/Sources/ChatMessageStickerItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageStickerItemNode.swift index 3785c23349..a360f6a152 100644 --- a/submodules/TelegramUI/Sources/ChatMessageStickerItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageStickerItemNode.swift @@ -1339,7 +1339,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView { if let replyInfoNode = self.replyInfoNode { let localRect = self.contextSourceNode.contentNode.view.convert(sourceReplyPanel.relativeSourceRect, to: replyInfoNode.view) - let offset = replyInfoNode.animateFromInputPanel(sourceReplyPanel: sourceReplyPanel, localRect: localRect, transition: transition) + let offset = replyInfoNode.animateFromInputPanel(sourceReplyPanel: sourceReplyPanel, localRect: localRect, horizontalTransition: transition, verticalTransition: transition) if let replyBackgroundNode = self.replyBackgroundNode { transition.animatePositionAdditive(node: replyBackgroundNode, offset: offset) replyBackgroundNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1) diff --git a/submodules/TelegramUI/Sources/ChatMessageTransitionNode.swift b/submodules/TelegramUI/Sources/ChatMessageTransitionNode.swift index 518f584a25..4c5d935b5e 100644 --- a/submodules/TelegramUI/Sources/ChatMessageTransitionNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageTransitionNode.swift @@ -157,16 +157,23 @@ final class ChatMessageTransitionNode: ASDisplayNode { private let itemNode: ChatMessageItemView private let contextSourceNode: ContextExtractedContentContainingNode private let source: ChatMessageTransitionNode.Source + private let getContentAreaInScreenSpace: () -> CGRect private let scrollingContainer: ASDisplayNode private let containerNode: ASDisplayNode + private let clippingNode: ASDisplayNode weak var overlayController: OverlayTransitionContainerController? var animationEnded: (() -> Void)? - init(itemNode: ChatMessageItemView, contextSourceNode: ContextExtractedContentContainingNode, source: ChatMessageTransitionNode.Source) { + init(itemNode: ChatMessageItemView, contextSourceNode: ContextExtractedContentContainingNode, source: ChatMessageTransitionNode.Source, getContentAreaInScreenSpace: @escaping () -> CGRect) { self.itemNode = itemNode + self.getContentAreaInScreenSpace = getContentAreaInScreenSpace + + self.clippingNode = ASDisplayNode() + self.clippingNode.clipsToBounds = true + self.scrollingContainer = ASDisplayNode() self.containerNode = ASDisplayNode() self.contextSourceNode = contextSourceNode @@ -174,7 +181,8 @@ final class ChatMessageTransitionNode: ASDisplayNode { super.init() - self.addSubnode(self.scrollingContainer) + self.addSubnode(self.clippingNode) + self.clippingNode.addSubnode(self.scrollingContainer) self.scrollingContainer.addSubnode(self.containerNode) } @@ -182,20 +190,43 @@ final class ChatMessageTransitionNode: ASDisplayNode { self.contextSourceNode.addSubnode(self.contextSourceNode.contentNode) } + func updateLayout(size: CGSize) { + self.clippingNode.frame = CGRect(origin: CGPoint(), size: size) + } + func beginAnimation() { let verticalDuration: Double = 0.5 let horizontalDuration: Double = verticalDuration * 0.5 let delay: Double = 0.0 + var updatedContentAreaInScreenSpace = self.getContentAreaInScreenSpace() + updatedContentAreaInScreenSpace.origin.x = 0.0 + updatedContentAreaInScreenSpace.size.width = self.clippingNode.bounds.width + + let timingFunction = CAMediaTimingFunction(controlPoints: 0.33, 0.0, 0.0, 1.0) + + let clippingOffset = updatedContentAreaInScreenSpace.minY - self.clippingNode.frame.minY + self.clippingNode.frame = CGRect(origin: CGPoint(x: 0.0, y: updatedContentAreaInScreenSpace.minY), size: self.clippingNode.bounds.size) + self.clippingNode.bounds = CGRect(origin: CGPoint(x: 0.0, y: clippingOffset), size: self.clippingNode.bounds.size) + + //self.clippingNode.layer.animateFrame(from: self.clippingNode.frame, to: updatedContentAreaInScreenSpace, duration: verticalDuration, mediaTimingFunction: timingFunction, removeOnCompletion: false) + //self.clippingNode.layer.animateBoundsOriginYAdditive(from: 0.0, to: updatedContentAreaInScreenSpace.minY, duration: verticalDuration, mediaTimingFunction: timingFunction, removeOnCompletion: false) + switch self.source { - case let .textInput(textInput, replyPanel): + case let .textInput(initialTextInput, replyPanel): self.contextSourceNode.isExtractedToContextPreview = true self.contextSourceNode.isExtractedToContextPreviewUpdated?(true) self.containerNode.addSubnode(self.contextSourceNode.contentNode) let targetAbsoluteRect = self.contextSourceNode.view.convert(self.contextSourceNode.contentRect, to: nil) - let sourceAbsoluteRect = textInput.backgroundView.frame.offsetBy(dx: textInput.sourceRect.minX, dy: textInput.sourceRect.minY) + let sourceBackgroundAbsoluteRect = initialTextInput.backgroundView.frame.offsetBy(dx: initialTextInput.sourceRect.minX, dy: initialTextInput.sourceRect.minY) + let sourceAbsoluteRect = CGRect(origin: CGPoint(x: sourceBackgroundAbsoluteRect.minX, y: sourceBackgroundAbsoluteRect.maxY - self.contextSourceNode.contentRect.height), size: self.contextSourceNode.contentRect.size) + + let textInput = ChatMessageTransitionNode.Source.TextInput(backgroundView: initialTextInput.backgroundView, contentView: initialTextInput.contentView, sourceRect: initialTextInput.sourceRect) + + textInput.backgroundView.frame = CGRect(origin: CGPoint(x: 0.0, y: sourceAbsoluteRect.height - sourceBackgroundAbsoluteRect.height), size: textInput.backgroundView.bounds.size) + textInput.contentView.frame = textInput.contentView.frame.offsetBy(dx: 0.0, dy: sourceAbsoluteRect.height - sourceBackgroundAbsoluteRect.height) var sourceReplyPanel: ReplyPanel? if let replyPanel = replyPanel, let replyPanelParentView = replyPanel.view.superview { @@ -209,11 +240,12 @@ final class ChatMessageTransitionNode: ASDisplayNode { self.itemNode.cancelInsertionAnimations() let transition: ContainedViewLayoutTransition = .animated(duration: horizontalDuration, curve: .custom(0.33, 0.0, 0.0, 1.0)) + let verticalTransition: ContainedViewLayoutTransition = .animated(duration: verticalDuration, curve: .custom(0.33, 0.0, 0.0, 1.0)) if let itemNode = self.itemNode as? ChatMessageBubbleItemNode { - itemNode.animateContentFromTextInputField(textInput: textInput, transition: transition) + itemNode.animateContentFromTextInputField(textInput: textInput, horizontalTransition: transition, verticalTransition: verticalTransition) if let sourceReplyPanel = sourceReplyPanel { - itemNode.animateReplyPanel(sourceReplyPanel: sourceReplyPanel, transition: transition) + itemNode.animateReplyPanel(sourceReplyPanel: sourceReplyPanel, horizontalTransition: transition, verticalTransition: verticalTransition) } } else if let itemNode = self.itemNode as? ChatMessageAnimatedStickerItemNode { itemNode.animateContentFromTextInputField(textInput: textInput, transition: transition) @@ -229,15 +261,15 @@ final class ChatMessageTransitionNode: ASDisplayNode { self.containerNode.frame = targetAbsoluteRect.offsetBy(dx: -self.contextSourceNode.contentRect.minX, dy: -self.contextSourceNode.contentRect.minY) self.contextSourceNode.updateAbsoluteRect?(self.containerNode.frame, UIScreen.main.bounds.size) - self.containerNode.layer.animatePosition(from: CGPoint(x: 0.0, y: sourceAbsoluteRect.minY - targetAbsoluteRect.minY), to: CGPoint(), duration: verticalDuration, delay: delay, mediaTimingFunction: CAMediaTimingFunction(controlPoints: 0.33, 0.0, 0.0, 1.0), additive: true, force: true, completion: { [weak self] _ in + self.containerNode.layer.animatePosition(from: CGPoint(x: 0.0, y: sourceAbsoluteRect.maxY - targetAbsoluteRect.maxY), to: CGPoint(), duration: verticalDuration, delay: delay, mediaTimingFunction: CAMediaTimingFunction(controlPoints: 0.33, 0.0, 0.0, 1.0), additive: true, force: true, completion: { [weak self] _ in guard let strongSelf = self else { return } strongSelf.endAnimation() }) - self.contextSourceNode.applyAbsoluteOffset?(CGPoint(x: sourceAbsoluteRect.minX - targetAbsoluteRect.minX, y: 0.0), .custom(0.33, 0.0, 0.0, 1.0), horizontalDuration) - self.contextSourceNode.applyAbsoluteOffset?(CGPoint(x: 0.0, y: sourceAbsoluteRect.minY - targetAbsoluteRect.minY), .custom(0.33, 0.0, 0.0, 1.0), verticalDuration) self.containerNode.layer.animatePosition(from: CGPoint(x: sourceAbsoluteRect.minX - targetAbsoluteRect.minX, y: 0.0), to: CGPoint(), duration: horizontalDuration, delay: delay, mediaTimingFunction: CAMediaTimingFunction(controlPoints: 0.33, 0.0, 0.0, 1.0), additive: true) + self.contextSourceNode.applyAbsoluteOffset?(CGPoint(x: sourceAbsoluteRect.minX - targetAbsoluteRect.minX, y: 0.0), .custom(0.33, 0.0, 0.0, 1.0), horizontalDuration) + self.contextSourceNode.applyAbsoluteOffset?(CGPoint(x: 0.0, y: sourceAbsoluteRect.maxY - targetAbsoluteRect.maxY), .custom(0.33, 0.0, 0.0, 1.0), verticalDuration) case let .stickerMediaInput(stickerMediaInput, replyPanel): self.itemNode.cancelInsertionAnimations() @@ -398,13 +430,31 @@ final class ChatMessageTransitionNode: ASDisplayNode { applyOffset = true } if applyOffset { + if transition.isAnimated { + assert(true) + } self.scrollingContainer.bounds = self.scrollingContainer.bounds.offsetBy(dx: 0.0, dy: -offset) transition.animateOffsetAdditive(node: self.scrollingContainer, offset: offset) } } + + func addContentOffset(offset: CGFloat, itemNode: ListViewItemNode?) { + var applyOffset = false + if let itemNode = itemNode { + if itemNode === self.itemNode { + applyOffset = true + } + } else { + applyOffset = true + } + if applyOffset { + self.scrollingContainer.bounds = self.scrollingContainer.bounds.offsetBy(dx: 0.0, dy: offset) + } + } } private let listNode: ChatHistoryListNode + private let getContentAreaInScreenSpace: () -> CGRect private var currentPendingItem: (Int64, Source, () -> Void)? @@ -414,8 +464,9 @@ final class ChatMessageTransitionNode: ASDisplayNode { return self.currentPendingItem != nil } - init(listNode: ChatHistoryListNode) { + init(listNode: ChatHistoryListNode, getContentAreaInScreenSpace: @escaping () -> CGRect) { self.listNode = listNode + self.getContentAreaInScreenSpace = getContentAreaInScreenSpace super.init() @@ -449,7 +500,9 @@ final class ChatMessageTransitionNode: ASDisplayNode { } if let contextSourceNode = contextSourceNode { - let animatingItemNode = AnimatingItemNode(itemNode: itemNode, contextSourceNode: contextSourceNode, source: source) + let animatingItemNode = AnimatingItemNode(itemNode: itemNode, contextSourceNode: contextSourceNode, source: source, getContentAreaInScreenSpace: self.getContentAreaInScreenSpace) + animatingItemNode.updateLayout(size: self.bounds.size) + self.animatingItemNodes.append(animatingItemNode) switch source { case .audioMicInput, .videoMessage: @@ -486,4 +539,10 @@ final class ChatMessageTransitionNode: ASDisplayNode { animatingItemNode.addExternalOffset(offset: offset, transition: transition, itemNode: itemNode) } } + + func addContentOffset(offset: CGFloat, itemNode: ListViewItemNode?) { + for animatingItemNode in self.animatingItemNodes { + animatingItemNode.addContentOffset(offset: offset, itemNode: itemNode) + } + } } diff --git a/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift b/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift index 431367db0f..c4eaff19ee 100644 --- a/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift @@ -137,8 +137,10 @@ private final class AccessoryItemIconButtonNode: HighlightTrackingButtonNode { } } +private let minInputFontSize: CGFloat = 5.0 + private func calclulateTextFieldMinHeight(_ presentationInterfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics) -> CGFloat { - let baseFontSize = max(17.0, presentationInterfaceState.fontSize.baseDisplaySize) + let baseFontSize = max(minInputFontSize, presentationInterfaceState.fontSize.baseDisplaySize) var result: CGFloat if baseFontSize.isEqual(to: 26.0) { result = 42.0 @@ -161,6 +163,26 @@ private func calclulateTextFieldMinHeight(_ presentationInterfaceState: ChatPres return result } +private func calculateTextFieldRealInsets(_ presentationInterfaceState: ChatPresentationInterfaceState) -> UIEdgeInsets { + let baseFontSize = max(minInputFontSize, presentationInterfaceState.fontSize.baseDisplaySize) + let top: CGFloat + let bottom: CGFloat + if baseFontSize.isEqual(to: 14.0) { + top = 2.0 + bottom = 1.0 + } else if baseFontSize.isEqual(to: 15.0) { + top = 1.0 + bottom = 1.0 + } else if baseFontSize.isEqual(to: 16.0) { + top = 0.5 + bottom = 0.0 + } else { + top = 0.0 + bottom = 0.0 + } + return UIEdgeInsets(top: 4.5 + top, left: 0.0, bottom: 5.5 + bottom, right: 0.0) +} + private var currentTextInputBackgroundImage: (UIColor, UIColor, CGFloat, UIImage)? private func textInputBackgroundImage(backgroundColor: UIColor?, inputBackgroundColor: UIColor?, strokeColor: UIColor, diameter: CGFloat) -> UIImage? { if let backgroundColor = backgroundColor, let current = currentTextInputBackgroundImage { @@ -362,7 +384,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate { if let presentationInterfaceState = self.presentationInterfaceState { textColor = presentationInterfaceState.theme.chat.inputPanel.inputTextColor accentTextColor = presentationInterfaceState.theme.chat.inputPanel.panelControlAccentColor - baseFontSize = max(17.0, presentationInterfaceState.fontSize.baseDisplaySize) + baseFontSize = max(minInputFontSize, presentationInterfaceState.fontSize.baseDisplaySize) } textInputNode.attributedText = textAttributedStringForStateText(state.inputText, fontSize: baseFontSize, textColor: textColor, accentTextColor: accentTextColor, writingDirection: nil) textInputNode.selectedRange = NSMakeRange(state.selectionRange.lowerBound, state.selectionRange.count) @@ -390,7 +412,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate { var baseFontSize: CGFloat = 17.0 if let presentationInterfaceState = self.presentationInterfaceState { textColor = presentationInterfaceState.theme.chat.inputPanel.inputTextColor - baseFontSize = max(17.0, presentationInterfaceState.fontSize.baseDisplaySize) + baseFontSize = max(minInputFontSize, presentationInterfaceState.fontSize.baseDisplaySize) } textInputNode.attributedText = NSAttributedString(string: value, font: Font.regular(baseFontSize), textColor: textColor) self.editableTextNodeDidUpdateText(textInputNode) @@ -399,7 +421,6 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate { } private let textInputViewInternalInsets = UIEdgeInsets(top: 1.0, left: 13.0, bottom: 1.0, right: 13.0) - private let textInputViewRealInsets = UIEdgeInsets(top: 4.5, left: 0.0, bottom: 5.5, right: 0.0) private let accessoryButtonSpacing: CGFloat = 0.0 private let accessoryButtonInset: CGFloat = 2.0 @@ -556,7 +577,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate { if let presentationInterfaceState = self.presentationInterfaceState { textColor = presentationInterfaceState.theme.chat.inputPanel.inputTextColor tintColor = presentationInterfaceState.theme.list.itemAccentColor - baseFontSize = max(17.0, presentationInterfaceState.fontSize.baseDisplaySize) + baseFontSize = max(minInputFontSize, presentationInterfaceState.fontSize.baseDisplaySize) keyboardAppearance = presentationInterfaceState.theme.rootController.keyboardColor.keyboardAppearance } @@ -567,13 +588,12 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate { paragraphStyle.maximumLineHeight = 20.0 paragraphStyle.minimumLineHeight = 20.0 - textInputNode.typingAttributes = [NSAttributedString.Key.font.rawValue: Font.regular(max(17.0, baseFontSize)), NSAttributedString.Key.foregroundColor.rawValue: textColor, NSAttributedString.Key.paragraphStyle.rawValue: paragraphStyle] + textInputNode.typingAttributes = [NSAttributedString.Key.font.rawValue: Font.regular(max(minInputFontSize, baseFontSize)), NSAttributedString.Key.foregroundColor.rawValue: textColor, NSAttributedString.Key.paragraphStyle.rawValue: paragraphStyle] textInputNode.clipsToBounds = false textInputNode.textView.clipsToBounds = false textInputNode.delegate = self textInputNode.hitTestSlop = UIEdgeInsets(top: -5.0, left: -5.0, bottom: -5.0, right: -5.0) textInputNode.keyboardAppearance = keyboardAppearance - textInputNode.textContainerInset = UIEdgeInsets(top: self.textInputViewRealInsets.top, left: 0.0, bottom: self.textInputViewRealInsets.bottom, right: 0.0) textInputNode.tintColor = tintColor textInputNode.textView.scrollIndicatorInsets = UIEdgeInsets(top: 9.0, left: 0.0, bottom: 9.0, right: -13.0) self.textInputContainer.addSubnode(textInputNode) @@ -582,6 +602,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate { if let presentationInterfaceState = self.presentationInterfaceState { refreshChatTextInputTypingAttributes(textInputNode, theme: presentationInterfaceState.theme, baseFontSize: baseFontSize) + textInputNode.textContainerInset = calculateTextFieldRealInsets(presentationInterfaceState) } if !self.textInputContainer.bounds.size.width.isZero { @@ -752,7 +773,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate { if self.theme == nil || !self.theme!.chat.inputPanel.inputTextColor.isEqual(interfaceState.theme.chat.inputPanel.inputTextColor) { let textColor = interfaceState.theme.chat.inputPanel.inputTextColor - let baseFontSize = max(17.0, interfaceState.fontSize.baseDisplaySize) + let baseFontSize = max(minInputFontSize, interfaceState.fontSize.baseDisplaySize) if let textInputNode = self.textInputNode { if let text = textInputNode.attributedText?.string { @@ -849,7 +870,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate { } if self.currentPlaceholder != placeholder || themeUpdated { self.currentPlaceholder = placeholder - let baseFontSize = max(17.0, interfaceState.fontSize.baseDisplaySize) + let baseFontSize = max(minInputFontSize, interfaceState.fontSize.baseDisplaySize) self.textPlaceholderNode.attributedText = NSAttributedString(string: placeholder, font: Font.regular(baseFontSize), textColor: interfaceState.theme.chat.inputPanel.inputPlaceholderColor) self.textInputNode?.textView.accessibilityHint = placeholder let placeholderSize = self.textPlaceholderNode.updateLayout(CGSize(width: 320.0, height: CGFloat.greatestFiniteMagnitude)) @@ -1278,6 +1299,11 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate { if let image = self.searchLayoutClearImageNode.image { self.searchLayoutClearImageNode.frame = CGRect(origin: CGPoint(x: floor((searchLayoutClearButtonSize.width - image.size.width) / 2.0), y: floor((searchLayoutClearButtonSize.height - image.size.height) / 2.0)), size: image.size) } + + var textInputViewRealInsets = UIEdgeInsets() + if let presentationInterfaceState = self.presentationInterfaceState { + textInputViewRealInsets = calculateTextFieldRealInsets(presentationInterfaceState) + } let textInputFrame = CGRect(x: leftInset + textFieldInsets.left, y: textFieldInsets.top, width: baseWidth - textFieldInsets.left - textFieldInsets.right + textInputBackgroundWidthOffset, height: panelHeight - textFieldInsets.top - textFieldInsets.bottom) transition.updateFrame(node: self.textInputContainer, frame: textInputFrame) @@ -1310,7 +1336,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate { let _ = placeholderApply() - contextPlaceholderNode.frame = CGRect(origin: CGPoint(x: leftInset + textFieldInsets.left + self.textInputViewInternalInsets.left, y: textFieldInsets.top + self.textInputViewInternalInsets.top + self.textInputViewRealInsets.top + UIScreenPixel), size: placeholderSize.size) + contextPlaceholderNode.frame = CGRect(origin: CGPoint(x: leftInset + textFieldInsets.left + self.textInputViewInternalInsets.left, y: textFieldInsets.top + self.textInputViewInternalInsets.top + textInputViewRealInsets.top + UIScreenPixel), size: placeholderSize.size) contextPlaceholderNode.alpha = audioRecordingItemsAlpha } else if let contextPlaceholderNode = self.contextPlaceholderNode { self.contextPlaceholderNode = nil @@ -1327,7 +1353,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate { self.slowmodePlaceholderNode = slowmodePlaceholderNode self.insertSubnode(slowmodePlaceholderNode, aboveSubnode: self.textPlaceholderNode) } - let placeholderFrame = CGRect(origin: CGPoint(x: leftInset + textFieldInsets.left + self.textInputViewInternalInsets.left, y: textFieldInsets.top + self.textInputViewInternalInsets.top + self.textInputViewRealInsets.top + UIScreenPixel), size: CGSize(width: width - leftInset - rightInset - textFieldInsets.left - textFieldInsets.right - self.textInputViewInternalInsets.left - self.textInputViewInternalInsets.right - accessoryButtonsWidth, height: 30.0)) + let placeholderFrame = CGRect(origin: CGPoint(x: leftInset + textFieldInsets.left + self.textInputViewInternalInsets.left, y: textFieldInsets.top + self.textInputViewInternalInsets.top + textInputViewRealInsets.top + UIScreenPixel), size: CGSize(width: width - leftInset - rightInset - textFieldInsets.left - textFieldInsets.right - self.textInputViewInternalInsets.left - self.textInputViewInternalInsets.right - accessoryButtonsWidth, height: 30.0)) slowmodePlaceholderNode.updateState(slowmodeState) slowmodePlaceholderNode.frame = placeholderFrame slowmodePlaceholderNode.alpha = audioRecordingItemsAlpha @@ -1350,7 +1376,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate { self.slowmodePlaceholderNode?.isHidden = true } - transition.updateFrame(node: self.textPlaceholderNode, frame: CGRect(origin: CGPoint(x: leftInset + textFieldInsets.left + self.textInputViewInternalInsets.left, y: textFieldInsets.top + self.textInputViewInternalInsets.top + self.textInputViewRealInsets.top + UIScreenPixel), size: self.textPlaceholderNode.frame.size)) + transition.updateFrame(node: self.textPlaceholderNode, frame: CGRect(origin: CGPoint(x: leftInset + textFieldInsets.left + self.textInputViewInternalInsets.left, y: textFieldInsets.top + self.textInputViewInternalInsets.top + textInputViewRealInsets.top + UIScreenPixel), size: self.textPlaceholderNode.frame.size)) transition.updateAlpha(node: self.textPlaceholderNode, alpha: audioRecordingItemsAlpha) transition.updateFrame(layer: self.textInputBackgroundNode.layer, frame: CGRect(x: leftInset + textFieldInsets.left, y: textFieldInsets.top, width: baseWidth - textFieldInsets.left - textFieldInsets.right + textInputBackgroundWidthOffset, height: panelHeight - textFieldInsets.top - textFieldInsets.bottom)) @@ -1492,7 +1518,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate { @objc func editableTextNodeDidUpdateText(_ editableTextNode: ASEditableTextNode) { if let textInputNode = self.textInputNode, let presentationInterfaceState = self.presentationInterfaceState { - let baseFontSize = max(17.0, presentationInterfaceState.fontSize.baseDisplaySize) + let baseFontSize = max(minInputFontSize, presentationInterfaceState.fontSize.baseDisplaySize) refreshChatTextInputAttributes(textInputNode, theme: presentationInterfaceState.theme, baseFontSize: baseFontSize) refreshChatTextInputTypingAttributes(textInputNode, theme: presentationInterfaceState.theme, baseFontSize: baseFontSize) @@ -1776,7 +1802,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate { UIMenuController.shared.update() } - let baseFontSize = max(17.0, presentationInterfaceState.fontSize.baseDisplaySize) + let baseFontSize = max(minInputFontSize, presentationInterfaceState.fontSize.baseDisplaySize) refreshChatTextInputTypingAttributes(textInputNode, theme: presentationInterfaceState.theme, baseFontSize: baseFontSize) } } @@ -1919,7 +1945,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate { if let presentationInterfaceState = self.presentationInterfaceState { textColor = presentationInterfaceState.theme.chat.inputPanel.inputTextColor accentTextColor = presentationInterfaceState.theme.chat.inputPanel.panelControlAccentColor - baseFontSize = max(17.0, presentationInterfaceState.fontSize.baseDisplaySize) + baseFontSize = max(minInputFontSize, presentationInterfaceState.fontSize.baseDisplaySize) } let cleanReplacementString = textAttributedStringForStateText(NSAttributedString(string: cleanText), fontSize: baseFontSize, textColor: textColor, accentTextColor: accentTextColor, writingDirection: nil) string.replaceCharacters(in: range, with: cleanReplacementString) diff --git a/submodules/TelegramUI/Sources/PeerInfo/Panes/PeerInfoGroupsInCommonPaneNode.swift b/submodules/TelegramUI/Sources/PeerInfo/Panes/PeerInfoGroupsInCommonPaneNode.swift index 0b8fb8e55e..f8ede6a784 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/Panes/PeerInfoGroupsInCommonPaneNode.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/Panes/PeerInfoGroupsInCommonPaneNode.swift @@ -146,7 +146,7 @@ final class PeerInfoGroupsInCommonPaneNode: ASDisplayNode, PeerInfoPaneNode { var scrollToItem: ListViewScrollToItem? if isScrollingLockedAtTop { switch self.listNode.visibleContentOffset() { - case .known(0.0): + case let .known(value) where value <= CGFloat.ulpOfOne: break default: scrollToItem = ListViewScrollToItem(index: 0, position: .top(0.0), animated: true, curve: .Spring(duration: duration), directionHint: .Up) diff --git a/submodules/TelegramUI/Sources/PeerInfo/Panes/PeerInfoListPaneNode.swift b/submodules/TelegramUI/Sources/PeerInfo/Panes/PeerInfoListPaneNode.swift index 681952c875..12d12c20fb 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/Panes/PeerInfoListPaneNode.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/Panes/PeerInfoListPaneNode.swift @@ -391,7 +391,9 @@ final class PeerInfoListPaneNode: ASDisplayNode, PeerInfoPaneNode { self.listNode.updateLayout(transition: transition, updateSizeAndInsets: ListViewUpdateSizeAndInsets(size: size, insets: UIEdgeInsets(top: topPanelHeight, left: sideInset, bottom: bottomInset, right: sideInset), duration: duration, curve: curve)) if isScrollingLockedAtTop { switch self.listNode.visibleContentOffset() { - case .known(0.0), .none: + case let .known(value) where value <= CGFloat.ulpOfOne: + break + case .none: break default: self.listNode.scrollToEndOfHistory() diff --git a/submodules/TelegramUI/Sources/PeerInfo/Panes/PeerInfoMembersPane.swift b/submodules/TelegramUI/Sources/PeerInfo/Panes/PeerInfoMembersPane.swift index f3918b0bc2..0cdb4c218b 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/Panes/PeerInfoMembersPane.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/Panes/PeerInfoMembersPane.swift @@ -191,7 +191,7 @@ final class PeerInfoMembersPaneNode: ASDisplayNode, PeerInfoPaneNode { var scrollToItem: ListViewScrollToItem? if isScrollingLockedAtTop { switch self.listNode.visibleContentOffset() { - case .known(0.0): + case let .known(value) where value <= CGFloat.ulpOfOne: break default: scrollToItem = ListViewScrollToItem(index: 0, position: .top(0.0), animated: true, curve: .Spring(duration: duration), directionHint: .Up)