Skip to content

Commit b0e60f6

Browse files
authored
fix: attaching KeyboardTrackingView (#1193)
## 📜 Description Fixed a problem of non working `onMove` handler in development builds. ## 💡 Motivation and Context In #1184 I started to rely that `didBecomeActive` event will be caught and we can attach the view from this listener. But turns out that in dev builds we miss this event. So at the moment: - view hierarchy may be not ready by time of creating `KeyboardTrackingView` (noticeable in release versions); - after `didBecomeActive` hierarchy is ready (but we may miss this event if we subscribed later, and also we rely on sequence of Apple events and it's kind of transitive dependencies that may break at any point of time). So I decided to re-work the approach. We attach `KeyboardControllerView` anyway, so I thought it's good to add a side effect in `didMoveToWindow` and attach our `KeyboardTrackingView`. For that I added optional param to `attachToTopmostView` method (called `toWindow/window`). If we pass the window then we use that param, otherwise rely on `activeWindow` and take `rootViewController.view`. Such combination seems to solve all these issues when `onMove` stops working under certain conditions. Now we always pass a window reference for a first mount and don't rely on internal Apple timing and events. Closes #1191 ## 📢 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 --> ### E2E - sync assets with working `onMove` handler on iOS 26; ### iOS - make `KeyboardTrackingView` public; - pass `toWindow/window` into `attachToTopmostView` method; - pass `window` into `attachToTopmostView` from `didMoveToWindow` lifecycle. ## 🤔 How Has This Been Tested? Tested: - iPhone 16 Pro, iOS 26 (XCode 16, physical device) - repro app (release/debug + cold start); ✅ - iPhone 16 Pro, iOS 26 (XCode 16, simulator) - example app (release/debug + cold start) + Modal in separate window example; ✅ - iPhone 16 Pro, iOS 26 (XCode 26, simulator) - example app (release/debug + cold start) + Modal in separate window example; ✅ ## 📸 Screenshots (if appropriate): |Before (debug)|After (debug)|Release repro| |-------|----|--------------| |<video src="https://github.com/user-attachments/assets/9af9992c-462c-45fc-8d2e-51d2fbe66dd9">|<video src="https://github.com/user-attachments/assets/7dcabd42-7310-4251-9870-29ba7c073517">|<video src="https://github.com/user-attachments/assets/68bd986f-7c1e-414f-a492-9178d72d5b8a">| ## 📝 Checklist - [x] CI successfully passed - [x] I added new mocks and corresponding unit-tests if library API was changed
1 parent 2bfe0c9 commit b0e60f6

9 files changed

Lines changed: 20 additions & 11 deletions
1.34 KB
Loading
-361 Bytes
Loading
1.34 KB
Loading
-172 Bytes
Loading
-646 Bytes
Loading

ios/observers/movement/KeyboardTrackingView.swift

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import UIKit
1111
* A compatibility view that resolves to `KeyboardView` on iOS < 26
1212
* and uses `keyboardLayoutGuide` on iOS 26+.
1313
*/
14-
final class KeyboardTrackingView: UIView {
14+
public final class KeyboardTrackingView: UIView {
1515
private var keyboardView: UIView? { KeyboardViewLocator.shared.resolve() }
1616
private var keyboardHeight = 0.0
1717
private weak var currentAttachedView: UIView?
@@ -56,23 +56,17 @@ final class KeyboardTrackingView: UIView {
5656
name: UIResponder.keyboardDidShowNotification,
5757
object: nil
5858
)
59-
NotificationCenter.default.addObserver(
60-
self,
61-
selector: #selector(attachToTopmostView),
62-
name: UIApplication.didBecomeActiveNotification,
63-
object: nil
64-
)
6559
}
6660

67-
override func willMove(toWindow newWindow: UIWindow?) {
61+
override public func willMove(toWindow newWindow: UIWindow?) {
6862
// When the view is being removed from the window, we need to re-attach it
6963
if newWindow == nil, !isAttaching {
7064
attachToTopmostView()
7165
}
7266
}
7367

74-
@objc private func attachToTopmostView() {
75-
guard let topView = UIApplication.topViewController()?.view else { return }
68+
@objc public func attachToTopmostView(toWindow window: UIWindow? = nil) {
69+
guard let topView = (window?.rootViewController ?? UIApplication.topViewController())?.view else { return }
7670

7771
if currentAttachedView === topView { return }
7872

ios/observers/movement/observer/KeyboardMovementObserver.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ public class KeyboardMovementObserver: NSObject {
1818
var onRequestAnimation: () -> Void
1919
var onCancelAnimation: () -> Void
2020
// progress tracker
21-
var keyboardTrackingView = KeyboardTrackingView()
21+
@objc public var keyboardTrackingView = KeyboardTrackingView()
2222
var animation: KeyboardAnimation?
2323

2424
var prevKeyboardPosition = 0.0

ios/views/KeyboardControllerView.mm

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,14 @@ - (void)willMoveToSuperview:(UIView *)newSuperview
259259
}
260260
}
261261

262+
- (void)didMoveToWindow
263+
{
264+
[super didMoveToWindow];
265+
if (self.window) {
266+
[keyboardObserver.keyboardTrackingView attachToTopmostViewToWindow:self.window];
267+
}
268+
}
269+
262270
- (void)mount
263271
{
264272
[inputObserver mount];

ios/views/KeyboardControllerViewManager.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,13 @@ class KeyboardControllerView: UIView {
8585
}
8686
}
8787

88+
override func didMoveToWindow() {
89+
super.didMoveToWindow()
90+
if window != nil {
91+
keyboardObserver?.keyboardTrackingView.attachToTopmostView(toWindow: window)
92+
}
93+
}
94+
8895
override func layoutSubviews() {
8996
super.layoutSubviews()
9097

0 commit comments

Comments
 (0)