Skip to content

Commit 3ab39a0

Browse files
authored
fix: attach keyboard tracking view in advance and only when window has been changed (#1170)
## 📜 Description Fixed strange animation introduced by `react-native-keyboard-controller`. ## 💡 Motivation and Context This regression was introduced in #1158 Basiucally I discovered 2 issues: - the tracking view could be attached later to the window so we could see "unreal" values, where keyboard height would be equal to whole screen size and `progress` could be 2+ #1166 (comment) - since we start to attach tracking view to each new window we can attach it to system alerts etc. #1169 So the manner that we choose was slightly agressive. The original issue was caused by fact that tracking view gets detached when modal appears. But when `Alert`/transparent `Modal` are shown, then we don't need to re-attach a veiw, because old view is not detached. Additionally I didin't like the fact that we subscribe to `didBecomeVisibleNotification` event, which gets triggered every time before the keyboard appears (potentially it may slow down the app). So in this PR I decided to re-work the approach. Now instead of "try to attach to top window root view" we follow approach "re-attach tracking view only if current tracking view was detached". And this approach works better because: - we don't attach listener for each new window (attach it only when necessary) - we attach it **before** keyboard starts its movement. So new solution seems to work better and fix all the issues caused by its previous implementation. Also I think it's worth to mention several solutions that didn't work: - I tried to use KVO on `view.window` property and it didn't give a desired behavior (listener was never called); - I tried to avoid using `keyboardLayoutGuide`, but when keyboard disappear - I again don't receive animation and animate instantly to the end position 🤷‍♂️ - I tried to attach tracking view to window but in this case it's not tracking any updates and it's not visible 🤷‍♂️ Closes #1169 ## 📢 Changelog <!-- High level overview of important changes --> <!-- For example: fixed status bar manipulation; added new types declarations; --> <!-- If your changes don't affect one of platform/language below - then remove this platform/language --> ### iOS - added `isAttaching` property (`willMove` can be called when we call `removeFromSuperview`/`addSubview` inside `attachToTopmostView` and we need to filter out these operations to avoid potential bugs; - replace `didBecomeVisibleNotification` mechanism with `willMove(toWindow`; - call `attachToTopmostView` pro-actively on first mount to start window observation. ## 🤔 How Has This Been Tested? Tested in example app + repro (iPhone 16 Pro - iOS 26.0, iPhone 14 Pro - iOS 16.4) ## 📸 Screenshots (if appropriate): |Before|After|Issue| |------|-----|------| |<video src="https://github.com/user-attachments/assets/0b7ab951-5a19-400e-b540-061df2c933a9">|<video src="https://github.com/user-attachments/assets/8653bee3-78c4-4b18-a6ea-312923779bd7">|https://github.com/kirillzyusko/react-native-keyboard-controller/issues/1166#issuecomment-3419679794| |<video src="https://github.com/user-attachments/assets/a48fb605-d7e9-484a-bec1-c87d601cdd85">|<video src="https://github.com/user-attachments/assets/614b9c78-b8ae-4918-9e06-8f18e1516a8f">|https://github.com/kirillzyusko/react-native-keyboard-controller/issues/1169| ## 📝 Checklist - [x] CI successfully passed - [x] I added new mocks and corresponding unit-tests if library API was changed
1 parent 0a6d012 commit 3ab39a0

File tree

1 file changed

+11
-6
lines changed

1 file changed

+11
-6
lines changed

ios/observers/movement/KeyboardTrackingView.swift

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ final class KeyboardTrackingView: UIView {
1515
private var keyboardView: UIView? { KeyboardViewLocator.shared.resolve() }
1616
private var keyboardHeight = 0.0
1717
private weak var currentAttachedView: UIView?
18+
private var isAttaching = false
1819

1920
static let invalidPosition: CGFloat = -.greatestFiniteMagnitude
2021

@@ -55,19 +56,22 @@ final class KeyboardTrackingView: UIView {
5556
name: UIResponder.keyboardDidShowNotification,
5657
object: nil
5758
)
58-
NotificationCenter.default.addObserver(
59-
self,
60-
selector: #selector(attachToTopmostView),
61-
name: UIWindow.didBecomeVisibleNotification,
62-
object: nil
63-
)
59+
attachToTopmostView()
60+
}
61+
62+
override func willMove(toWindow newWindow: UIWindow?) {
63+
// When the view is being removed from the window, we need to re-attach it
64+
if newWindow == nil, !isAttaching {
65+
attachToTopmostView()
66+
}
6467
}
6568

6669
@objc private func attachToTopmostView() {
6770
guard let topView = UIApplication.topViewController()?.view else { return }
6871

6972
if currentAttachedView === topView { return }
7073

74+
isAttaching = true
7175
removeFromSuperview()
7276

7377
topView.addSubview(self)
@@ -87,6 +91,7 @@ final class KeyboardTrackingView: UIView {
8791
heightAnchor.constraint(equalToConstant: 0),
8892
])
8993
}
94+
isAttaching = false
9095
}
9196

9297
@objc private func keyboardWillAppear(_ notification: Notification) {

0 commit comments

Comments
 (0)