mirror of
https://github.com/TelegramMessenger/Telegram-iOS.git
synced 2026-07-05 19:28:46 +02:00
Fix keyboard swipe-back handling
Keep the keyboard attached during interactive navigation pops by skipping the normal ChatController editing dismissal while the controller is being interactively popped. Cancel interactive keyboard gestures when a horizontal pop begins, so vertical jitter in the back-swipe gesture does not trigger keyboard-dismiss tracking during navigation. Force the delayed keyboard hide layout update after programmatic dismissal to use an immediate transition, avoiding visible movement after returning to the chat list.
This commit is contained in:
parent
6e370e06d1
commit
3306d4cf24
5 changed files with 46 additions and 19 deletions
|
|
@ -127,13 +127,6 @@ class KeyboardManager {
|
|||
}
|
||||
}
|
||||
|
||||
private func endAnimations(view: UIView) {
|
||||
view.layer.removeAllAnimations()
|
||||
for subview in view.subviews {
|
||||
endAnimations(view: subview)
|
||||
}
|
||||
}
|
||||
|
||||
public func viewTreeContainsFirstResponder(view: UIView) -> Bool {
|
||||
if view.isFirstResponder {
|
||||
return true
|
||||
|
|
@ -149,6 +142,7 @@ public func viewTreeContainsFirstResponder(view: UIView) -> Bool {
|
|||
|
||||
public final class KeyboardViewManager {
|
||||
private let host: StatusBarHost
|
||||
var willDismissEditingWithoutAnimation: (() -> Void)?
|
||||
|
||||
init(host: StatusBarHost) {
|
||||
self.host = host
|
||||
|
|
@ -156,11 +150,9 @@ public final class KeyboardViewManager {
|
|||
|
||||
public func dismissEditingWithoutAnimation(view: UIView) {
|
||||
if viewTreeContainsFirstResponder(view: view) {
|
||||
view.endEditing(true)
|
||||
if let keyboardWindow = self.host.keyboardWindow {
|
||||
for view in keyboardWindow.subviews {
|
||||
endAnimations(view: view)
|
||||
}
|
||||
self.willDismissEditingWithoutAnimation?()
|
||||
UIView.performWithoutAnimation {
|
||||
view.endEditing(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -233,6 +233,8 @@ public final class NavigationContainer: ASDisplayNode, ASGestureRecognizerDelega
|
|||
return
|
||||
}
|
||||
|
||||
topController.cancelInteractiveKeyboardGestures()
|
||||
topController.isBeingInteractivelyPopped = true
|
||||
topController.viewWillDisappear(true)
|
||||
let topNode = topController.displayNode
|
||||
var bottomControllerLayout = layout
|
||||
|
|
@ -299,6 +301,7 @@ public final class NavigationContainer: ASDisplayNode, ASGestureRecognizerDelega
|
|||
strongSelf.state.transition = nil
|
||||
|
||||
strongSelf.controllerRemoved(top.value)
|
||||
topController.isBeingInteractivelyPopped = false
|
||||
strongSelf.ignoreInputHeight = false
|
||||
})
|
||||
} else {
|
||||
|
|
@ -307,7 +310,8 @@ public final class NavigationContainer: ASDisplayNode, ASGestureRecognizerDelega
|
|||
return
|
||||
}
|
||||
strongSelf.state.transition = nil
|
||||
|
||||
|
||||
top.value.isBeingInteractivelyPopped = false
|
||||
top.value.viewDidAppear(true)
|
||||
transition.previous.value.viewDidDisappear(true)
|
||||
})
|
||||
|
|
|
|||
|
|
@ -234,6 +234,7 @@ public protocol CustomViewControllerNavigationDataSummary: AnyObject {
|
|||
private weak var activeInputView: UIResponder?
|
||||
|
||||
open var hasActiveInput: Bool = false
|
||||
public internal(set) var isBeingInteractivelyPopped: Bool = false
|
||||
|
||||
open var overlayWantsToBeBelowKeyboard: Bool {
|
||||
return false
|
||||
|
|
@ -657,6 +658,10 @@ public protocol CustomViewControllerNavigationDataSummary: AnyObject {
|
|||
open func viewWillLeaveNavigation() {
|
||||
}
|
||||
|
||||
open func cancelInteractiveKeyboardGestures() {
|
||||
self.view.windowHost?.cancelInteractiveKeyboardGestures()
|
||||
}
|
||||
|
||||
open override func viewDidAppear(_ animated: Bool) {
|
||||
self.activeInputView = nil
|
||||
|
||||
|
|
|
|||
|
|
@ -293,6 +293,7 @@ public class Window1 {
|
|||
private var statusBarHidden = false
|
||||
|
||||
private var shouldNotAnimateLikelyKeyboardAutocorrectionSwitch: Bool = false
|
||||
private var suppressNextKeyboardHideAnimationUntil: Double?
|
||||
|
||||
public private(set) var forceInCallStatusBarText: String? = nil
|
||||
public var inCallNavigate: (() -> Void)?
|
||||
|
|
@ -477,6 +478,10 @@ public class Window1 {
|
|||
self.topPresentationContext.containerLayoutUpdated(containedLayoutForWindowLayout(self.windowLayout, deviceMetrics: self.deviceMetrics), transition: .immediate)
|
||||
self.overlayPresentationContext.containerLayoutUpdated(containedLayoutForWindowLayout(self.windowLayout, deviceMetrics: self.deviceMetrics), transition: .immediate)
|
||||
|
||||
self.keyboardViewManager?.willDismissEditingWithoutAnimation = { [weak self] in
|
||||
self?.suppressNextKeyboardHideAnimationUntil = CACurrentMediaTime() + 1.0
|
||||
}
|
||||
|
||||
//TODO:release check old iOS
|
||||
/*self.statusBarChangeObserver = NotificationCenter.default.addObserver(forName: UIApplication.willChangeStatusBarFrameNotification, object: nil, queue: OperationQueue.main, using: { [weak self] notification in
|
||||
if let strongSelf = self, strongSelf.statusBarHost != nil {
|
||||
|
|
@ -648,6 +653,9 @@ public class Window1 {
|
|||
transition = .immediate
|
||||
}
|
||||
}
|
||||
if strongSelf.shouldSuppressNextKeyboardHideAnimation(keyboardHeight: keyboardHeight) {
|
||||
transition = .immediate
|
||||
}
|
||||
|
||||
strongSelf.updateLayout { $0.update(inputHeight: keyboardHeight.isLessThanOrEqualTo(0.0) ? nil : keyboardHeight, transition: transition, overrideTransition: false) }
|
||||
}
|
||||
|
|
@ -717,6 +725,21 @@ public class Window1 {
|
|||
self.hostView.containerView.addGestureRecognizer(recognizer)
|
||||
self.hostView.containerView.addSubview(self.badgeView)
|
||||
}
|
||||
|
||||
private func shouldSuppressNextKeyboardHideAnimation(keyboardHeight: CGFloat) -> Bool {
|
||||
guard let suppressNextKeyboardHideAnimationUntil = self.suppressNextKeyboardHideAnimationUntil else {
|
||||
return false
|
||||
}
|
||||
if suppressNextKeyboardHideAnimationUntil < CACurrentMediaTime() {
|
||||
self.suppressNextKeyboardHideAnimationUntil = nil
|
||||
return false
|
||||
}
|
||||
if keyboardHeight.isLessThanOrEqualTo(0.0) {
|
||||
self.suppressNextKeyboardHideAnimationUntil = nil
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
public required init(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
|
|
|
|||
|
|
@ -7967,12 +7967,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||
override public func viewWillDisappear(_ animated: Bool) {
|
||||
super.viewWillDisappear(animated)
|
||||
|
||||
if #available(iOS 18.0, *) {
|
||||
} else {
|
||||
//TODO:release
|
||||
}
|
||||
UIView.performWithoutAnimation {
|
||||
self.view.endEditing(true)
|
||||
if !self.isBeingInteractivelyPopped {
|
||||
UIView.performWithoutAnimation {
|
||||
self.view.endEditing(true)
|
||||
}
|
||||
}
|
||||
|
||||
self.chatDisplayNode.historyNode.canReadHistory.set(.single(false))
|
||||
|
|
@ -8048,6 +8046,11 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||
self.chatDisplayNode.willNavigateAway()
|
||||
}
|
||||
|
||||
override public func cancelInteractiveKeyboardGestures() {
|
||||
super.cancelInteractiveKeyboardGestures()
|
||||
self.chatDisplayNode.cancelInteractiveKeyboardGestures()
|
||||
}
|
||||
|
||||
override public func inFocusUpdated(isInFocus: Bool) {
|
||||
self.disableStickerAnimationsPromise.set(!isInFocus)
|
||||
self.chatDisplayNode.inFocusUpdated(isInFocus: isInFocus)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue