react-native-keyboard-controller 1.18.2 → 1.18.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (25) hide show
  1. package/android/src/main/java/com/reactnativekeyboardcontroller/modules/statusbar/StatusBarManagerCompatModuleImpl.kt +2 -3
  2. package/android/src/main/java/com/reactnativekeyboardcontroller/views/EdgeToEdgeReactViewGroup.kt +12 -5
  3. package/ios/observers/movement/KeyboardTrackingView.swift +139 -0
  4. package/ios/observers/movement/KeyboardViewLocator.swift +23 -0
  5. package/ios/observers/movement/observer/KeyboardMovementObserver+Animation.swift +21 -0
  6. package/ios/observers/movement/observer/KeyboardMovementObserver+Interactive.swift +56 -0
  7. package/ios/observers/movement/observer/KeyboardMovementObserver+Lifecycle.swift +47 -0
  8. package/ios/observers/movement/observer/KeyboardMovementObserver+Listeners.swift +88 -0
  9. package/ios/observers/movement/observer/KeyboardMovementObserver+Watcher.swift +72 -0
  10. package/ios/observers/movement/observer/KeyboardMovementObserver.swift +63 -0
  11. package/ios/views/KeyboardControllerView.mm +10 -10
  12. package/ios/views/KeyboardExtenderContainerView.swift +119 -42
  13. package/ios/views/KeyboardExtenderManager.mm +12 -9
  14. package/lib/commonjs/components/KeyboardAwareScrollView/index.js +0 -1
  15. package/lib/commonjs/components/KeyboardAwareScrollView/index.js.map +1 -1
  16. package/lib/commonjs/types/views.js.map +1 -1
  17. package/lib/module/components/KeyboardAwareScrollView/index.js +0 -1
  18. package/lib/module/components/KeyboardAwareScrollView/index.js.map +1 -1
  19. package/lib/module/types/views.js.map +1 -1
  20. package/lib/typescript/types/views.d.ts +1 -1
  21. package/package.json +1 -1
  22. package/src/components/KeyboardAwareScrollView/index.tsx +0 -2
  23. package/src/types/views.ts +1 -1
  24. package/ios/observers/KeyboardMovementObserver.swift +0 -325
  25. /package/ios/observers/{KeyboardEventsIgnorer.swift → movement/KeyboardEventsIgnorer.swift} +0 -0
@@ -9,9 +9,9 @@ import androidx.core.view.WindowInsetsCompat
9
9
  import androidx.core.view.WindowInsetsControllerCompat
10
10
  import com.facebook.react.bridge.ReactApplicationContext
11
11
  import com.facebook.react.bridge.UiThreadUtil
12
- import com.reactnativekeyboardcontroller.extensions.rootView
13
12
  import com.reactnativekeyboardcontroller.log.Logger
14
13
  import com.reactnativekeyboardcontroller.views.EdgeToEdgeReactViewGroup
14
+ import com.reactnativekeyboardcontroller.views.EdgeToEdgeViewRegistry
15
15
  import java.lang.ref.WeakReference
16
16
 
17
17
  private val TAG = StatusBarManagerCompatModuleImpl::class.qualifiedName
@@ -117,8 +117,7 @@ class StatusBarManagerCompatModuleImpl(
117
117
 
118
118
  private fun isEnabled(): Boolean = view()?.active ?: false
119
119
 
120
- private fun view(): EdgeToEdgeReactViewGroup? =
121
- mReactContext.rootView?.findViewWithTag<EdgeToEdgeReactViewGroup>(EdgeToEdgeReactViewGroup.VIEW_TAG)
120
+ private fun view(): EdgeToEdgeReactViewGroup? = EdgeToEdgeViewRegistry.get()
122
121
 
123
122
  companion object {
124
123
  const val NAME = "StatusBarManager"
@@ -19,9 +19,20 @@ import com.reactnativekeyboardcontroller.listeners.KeyboardAnimationCallback
19
19
  import com.reactnativekeyboardcontroller.listeners.KeyboardAnimationCallbackConfig
20
20
  import com.reactnativekeyboardcontroller.log.Logger
21
21
  import com.reactnativekeyboardcontroller.modal.ModalAttachedWatcher
22
+ import java.lang.ref.WeakReference
22
23
 
23
24
  private val TAG = EdgeToEdgeReactViewGroup::class.qualifiedName
24
25
 
26
+ object EdgeToEdgeViewRegistry {
27
+ private var lastCreatedView: WeakReference<EdgeToEdgeReactViewGroup>? = null
28
+
29
+ fun register(view: EdgeToEdgeReactViewGroup) {
30
+ lastCreatedView = WeakReference(view)
31
+ }
32
+
33
+ fun get(): EdgeToEdgeReactViewGroup? = lastCreatedView?.get()
34
+ }
35
+
25
36
  @Suppress("detekt:TooManyFunctions")
26
37
  @SuppressLint("ViewConstructor")
27
38
  class EdgeToEdgeReactViewGroup(
@@ -58,7 +69,7 @@ class EdgeToEdgeReactViewGroup(
58
69
  private val modalAttachedWatcher = ModalAttachedWatcher(this, reactContext, config, ::getKeyboardCallback)
59
70
 
60
71
  init {
61
- tag = VIEW_TAG
72
+ EdgeToEdgeViewRegistry.register(this)
62
73
  }
63
74
 
64
75
  // region View life cycles
@@ -241,8 +252,4 @@ class EdgeToEdgeReactViewGroup(
241
252
  }
242
253
  }
243
254
  // endregion
244
-
245
- companion object {
246
- val VIEW_TAG = EdgeToEdgeReactViewGroup::class.simpleName
247
- }
248
255
  }
@@ -0,0 +1,139 @@
1
+ //
2
+ // KeyboardTrackingView.swift
3
+ // Pods
4
+ //
5
+ // Created by Kiryl Ziusko on 25/07/2025.
6
+ //
7
+
8
+ import UIKit
9
+
10
+ /**
11
+ * A compatibility view that resolves to `KeyboardView` on iOS < 26
12
+ * and uses `keyboardLayoutGuide` on iOS 26+.
13
+ */
14
+ final class KeyboardTrackingView: UIView {
15
+ private var keyboardView: UIView? { KeyboardViewLocator.shared.resolve() }
16
+ private var keyboardHeight = 0.0
17
+
18
+ static let invalidPosition: CGFloat = -.greatestFiniteMagnitude
19
+
20
+ override init(frame: CGRect) {
21
+ super.init(frame: frame)
22
+ setup()
23
+ }
24
+
25
+ required init?(coder: NSCoder) {
26
+ super.init(coder: coder)
27
+ setup()
28
+ }
29
+
30
+ required init() {
31
+ super.init(frame: .zero)
32
+ setup()
33
+ }
34
+
35
+ deinit {
36
+ NotificationCenter.default.removeObserver(self)
37
+ }
38
+
39
+ private func setup() {
40
+ // for debug purposes
41
+ // self.backgroundColor = .red
42
+ isUserInteractionEnabled = false
43
+ isHidden = true
44
+
45
+ NotificationCenter.default.addObserver(
46
+ self,
47
+ selector: #selector(keyboardWillAppear),
48
+ name: UIResponder.keyboardWillShowNotification,
49
+ object: nil
50
+ )
51
+ NotificationCenter.default.addObserver(
52
+ self,
53
+ selector: #selector(keyboardDidAppear),
54
+ name: UIResponder.keyboardDidShowNotification,
55
+ object: nil
56
+ )
57
+
58
+ guard
59
+ let window = UIApplication.shared.activeWindow,
60
+ let rootView = window.rootViewController?.view
61
+ else {
62
+ return
63
+ }
64
+
65
+ rootView.addSubview(self)
66
+
67
+ translatesAutoresizingMaskIntoConstraints = false
68
+
69
+ if #available(iOS 15.0, *) {
70
+ if #available(iOS 17.0, *) {
71
+ rootView.keyboardLayoutGuide.usesBottomSafeArea = false
72
+ }
73
+
74
+ NSLayoutConstraint.activate([
75
+ leadingAnchor.constraint(equalTo: rootView.leadingAnchor, constant: 0),
76
+ trailingAnchor.constraint(equalTo: rootView.trailingAnchor, constant: 0),
77
+ bottomAnchor.constraint(equalTo: rootView.keyboardLayoutGuide.topAnchor, constant: 0),
78
+ heightAnchor.constraint(equalToConstant: 0),
79
+ ])
80
+ }
81
+ }
82
+
83
+ @objc private func keyboardWillAppear(_ notification: Notification) {
84
+ updateHeightFromNotification(notification)
85
+ }
86
+
87
+ @objc private func keyboardDidAppear(_ notification: Notification) {
88
+ updateHeightFromNotification(notification)
89
+ }
90
+
91
+ private func updateHeightFromNotification(_ notification: Notification) {
92
+ let (_, frame) = notification.keyboardMetaData()
93
+ if let keyboardFrame = frame {
94
+ let keyboardHeight = keyboardFrame.cgRectValue.size.height
95
+ self.keyboardHeight = keyboardHeight
96
+ }
97
+ }
98
+
99
+ @objc var view: UIView? {
100
+ if #available(iOS 26.0, *) {
101
+ return self
102
+ } else {
103
+ return keyboardView
104
+ }
105
+ }
106
+
107
+ func interactive(point: CGPoint) -> CGFloat {
108
+ guard let trackedView = view else { return Self.invalidPosition }
109
+
110
+ let keyboardFrameY = point.y
111
+ let keyboardWindowH = trackedView.window?.bounds.size.height ?? 0
112
+ let keyboardPosition = keyboardWindowH - keyboardFrameY
113
+
114
+ // for `keyboardLayoutGuide` case we can just read keyboard position directly - no interpolation needed
115
+ if #available(iOS 26.0, *) {
116
+ // when we are the top position KVO takes `inputAccessoryView` into consideration,
117
+ // so we handle it here
118
+ if keyboardPosition == keyboardHeight {
119
+ return keyboardPosition - KeyboardAreaExtender.shared.offset
120
+ }
121
+ return keyboardPosition
122
+ }
123
+
124
+ // if keyboard height is not equal to its bounds - we can ignore
125
+ // values, since they'll be invalid and will cause UI jumps
126
+ // valid only for non-`keyboardLayoutGuide` case
127
+ if floor(trackedView.bounds.size.height) != floor(keyboardHeight) {
128
+ return Self.invalidPosition
129
+ }
130
+
131
+ let position = CGFloat.interpolate(
132
+ inputRange: [keyboardHeight / 2, -keyboardHeight / 2],
133
+ outputRange: [keyboardHeight, 0],
134
+ currentValue: keyboardPosition
135
+ )
136
+
137
+ return position - KeyboardAreaExtender.shared.offset
138
+ }
139
+ }
@@ -0,0 +1,23 @@
1
+ //
2
+ // KeyboardViewLocator.swift
3
+ // Pods
4
+ //
5
+ // Created by Kiryl Ziusko on 25/07/2025.
6
+ //
7
+
8
+ final class KeyboardViewLocator {
9
+ static let shared = KeyboardViewLocator()
10
+ private var cachedKeyboardView: UIView?
11
+ private var cachedWindowsCount: Int = 0
12
+
13
+ func resolve() -> UIView? {
14
+ let currentWindowsCount = UIApplication.shared.windows.count
15
+
16
+ if cachedKeyboardView == nil || currentWindowsCount != cachedWindowsCount {
17
+ cachedKeyboardView = KeyboardView.find()
18
+ cachedWindowsCount = currentWindowsCount
19
+ }
20
+
21
+ return cachedKeyboardView
22
+ }
23
+ }
@@ -0,0 +1,21 @@
1
+ //
2
+ // KeyboardMovementObserver+Animation.swift
3
+ // Pods
4
+ //
5
+ // Created by Kiryl Ziusko on 07/08/2025.
6
+ //
7
+
8
+ extension KeyboardMovementObserver {
9
+ func initializeAnimation(fromValue: Double, toValue: Double) {
10
+ for key in ["position", "opacity"] {
11
+ if let keyboardAnimation = keyboardTrackingView.view?.layer.presentation()?.animation(forKey: key) {
12
+ if let springAnimation = keyboardAnimation as? CASpringAnimation {
13
+ animation = SpringAnimation(animation: springAnimation, fromValue: fromValue, toValue: toValue)
14
+ } else if let basicAnimation = keyboardAnimation as? CABasicAnimation {
15
+ animation = TimingAnimation(animation: basicAnimation, fromValue: fromValue, toValue: toValue)
16
+ }
17
+ return
18
+ }
19
+ }
20
+ }
21
+ }
@@ -0,0 +1,56 @@
1
+ //
2
+ // KeyboardMovementObserver+Interactive.swift
3
+ // Pods
4
+ //
5
+ // Created by Kiryl Ziusko on 07/08/2025.
6
+ //
7
+
8
+ extension KeyboardMovementObserver {
9
+ func setupKVObserver() {
10
+ guard interactiveKeyboardObserver == nil, let view = keyboardTrackingView.view else { return }
11
+
12
+ interactiveKeyboardObserver = view.observe(\.center, options: .new) { [weak self] _, change in
13
+ guard let self = self, let changeValue = change.newValue else { return }
14
+
15
+ self.keyboardDidMoveInteractively(changeValue: changeValue)
16
+ }
17
+ }
18
+
19
+ func removeKVObserver() {
20
+ interactiveKeyboardObserver?.invalidate()
21
+ interactiveKeyboardObserver = nil
22
+ }
23
+
24
+ private func keyboardDidMoveInteractively(changeValue: CGPoint) {
25
+ if UIResponder.isKeyboardPreloading {
26
+ return
27
+ }
28
+ // if we are currently animating keyboard -> we need to ignore values from KVO
29
+ if !displayLink.isPaused {
30
+ return
31
+ }
32
+
33
+ let position = keyboardTrackingView.interactive(point: changeValue)
34
+
35
+ if position == KeyboardTrackingView.invalidPosition {
36
+ return
37
+ }
38
+
39
+ if position == 0 {
40
+ // it will be triggered before `keyboardWillDisappear` and
41
+ // we don't need to trigger `onInteractive` handler for that
42
+ // since it will be handled in `keyboardWillDisappear` function
43
+ return
44
+ }
45
+
46
+ prevKeyboardPosition = position
47
+
48
+ onEvent(
49
+ "onKeyboardMoveInteractive",
50
+ position as NSNumber,
51
+ position / CGFloat(keyboardHeight) as NSNumber,
52
+ -1,
53
+ tag
54
+ )
55
+ }
56
+ }
@@ -0,0 +1,47 @@
1
+ //
2
+ // KeyboardMovementObserver+Lifecycle.swift
3
+ // Pods
4
+ //
5
+ // Created by Kiryl Ziusko on 07/08/2025.
6
+ //
7
+
8
+ public extension KeyboardMovementObserver {
9
+ @objc func mount() {
10
+ if isMounted {
11
+ return
12
+ }
13
+
14
+ isMounted = true
15
+
16
+ NotificationCenter.default.addObserver(
17
+ self,
18
+ selector: #selector(keyboardWillDisappear),
19
+ name: UIResponder.keyboardWillHideNotification,
20
+ object: nil
21
+ )
22
+ NotificationCenter.default.addObserver(
23
+ self,
24
+ selector: #selector(keyboardWillAppear),
25
+ name: UIResponder.keyboardWillShowNotification,
26
+ object: nil
27
+ )
28
+ NotificationCenter.default.addObserver(
29
+ self,
30
+ selector: #selector(keyboardDidAppear),
31
+ name: UIResponder.keyboardDidShowNotification,
32
+ object: nil
33
+ )
34
+ NotificationCenter.default.addObserver(
35
+ self,
36
+ selector: #selector(keyboardDidDisappear),
37
+ name: UIResponder.keyboardDidHideNotification,
38
+ object: nil
39
+ )
40
+ }
41
+
42
+ @objc func unmount() {
43
+ isMounted = false
44
+ // swiftlint:disable:next notification_center_detachment
45
+ NotificationCenter.default.removeObserver(self)
46
+ }
47
+ }
@@ -0,0 +1,88 @@
1
+ //
2
+ // KeyboardMovementObserver+Listeners.swift
3
+ // Pods
4
+ //
5
+ // Created by Kiryl Ziusko on 07/08/2025.
6
+ //
7
+
8
+ extension KeyboardMovementObserver {
9
+ @objc func keyboardWillAppear(_ notification: Notification) {
10
+ guard !KeyboardEventsIgnorer.shared.shouldIgnore, !UIResponder.isKeyboardPreloading else { return }
11
+
12
+ let (duration, frame) = notification.keyboardMetaData()
13
+ if let keyboardFrame = frame {
14
+ tag = UIResponder.current.reactViewTag
15
+ let keyboardHeight = keyboardFrame.cgRectValue.size.height
16
+ self.keyboardHeight = keyboardHeight
17
+ self.duration = duration
18
+ didShowDeadline = Date.currentTimeStamp + Int64(duration)
19
+
20
+ onRequestAnimation()
21
+ onEvent("onKeyboardMoveStart", Float(self.keyboardHeight) as NSNumber, 1, duration as NSNumber, tag)
22
+ onNotify("KeyboardController::keyboardWillShow", buildEventParams(self.keyboardHeight, duration, tag))
23
+
24
+ setupKeyboardWatcher()
25
+ initializeAnimation(fromValue: prevKeyboardPosition, toValue: self.keyboardHeight)
26
+ }
27
+ }
28
+
29
+ @objc func keyboardWillDisappear(_ notification: Notification) {
30
+ guard !UIResponder.isKeyboardPreloading else { return }
31
+ let (duration, _) = notification.keyboardMetaData()
32
+ tag = UIResponder.current.reactViewTag
33
+ self.duration = duration
34
+
35
+ onRequestAnimation()
36
+ onEvent("onKeyboardMoveStart", 0, 0, duration as NSNumber, tag)
37
+ onNotify("KeyboardController::keyboardWillHide", buildEventParams(0, duration, tag))
38
+
39
+ setupKeyboardWatcher()
40
+ removeKVObserver()
41
+ initializeAnimation(fromValue: prevKeyboardPosition, toValue: 0)
42
+ }
43
+
44
+ @objc func keyboardDidAppear(_ notification: Notification) {
45
+ guard !UIResponder.isKeyboardPreloading else { return }
46
+
47
+ let timestamp = Date.currentTimeStamp
48
+ let (duration, frame) = notification.keyboardMetaData()
49
+ if let keyboardFrame = frame {
50
+ let (position, _) = keyboardTrackingView.view.frameTransitionInWindow
51
+ let keyboardHeight = keyboardFrame.cgRectValue.size.height
52
+ tag = UIResponder.current.reactViewTag
53
+ self.keyboardHeight = keyboardHeight
54
+
55
+ guard !KeyboardEventsIgnorer.shared.shouldIgnore else {
56
+ KeyboardEventsIgnorer.shared.shouldIgnoreKeyboardEvents = false
57
+ return
58
+ }
59
+
60
+ // if the event is caught in between it's highly likely that it could be a "resize" event
61
+ // so we just read actual keyboard frame value in this case
62
+ let height = timestamp >= didShowDeadline ? self.keyboardHeight : position - KeyboardAreaExtender.shared.offset
63
+ // always limit progress to the maximum possible value
64
+ let progress = min(height / self.keyboardHeight, 1.0)
65
+
66
+ onCancelAnimation()
67
+ onEvent("onKeyboardMoveEnd", height as NSNumber, progress as NSNumber, duration as NSNumber, tag)
68
+ onNotify("KeyboardController::keyboardDidShow", buildEventParams(height, duration, tag))
69
+
70
+ removeKeyboardWatcher()
71
+ setupKVObserver()
72
+ animation = nil
73
+ }
74
+ }
75
+
76
+ @objc func keyboardDidDisappear(_ notification: Notification) {
77
+ guard !UIResponder.isKeyboardPreloading else { return }
78
+ let (duration, _) = notification.keyboardMetaData()
79
+ tag = UIResponder.current.reactViewTag
80
+
81
+ onCancelAnimation()
82
+ onEvent("onKeyboardMoveEnd", 0 as NSNumber, 0, duration as NSNumber, tag)
83
+ onNotify("KeyboardController::keyboardDidHide", buildEventParams(0, duration, tag))
84
+
85
+ removeKeyboardWatcher()
86
+ animation = nil
87
+ }
88
+ }
@@ -0,0 +1,72 @@
1
+ //
2
+ // KeyboardMovementObserver+Watcher.swift
3
+ // Pods
4
+ //
5
+ // Created by Kiryl Ziusko on 07/08/2025.
6
+ //
7
+
8
+ extension KeyboardMovementObserver {
9
+ @objc func setupKeyboardWatcher() {
10
+ // sometimes `will` events can be called multiple times.
11
+ // To avoid double re-creation of listener we are adding this condition
12
+ // (if active link is present, then no need to re-setup a listener)
13
+ if !displayLink.isPaused {
14
+ return
15
+ }
16
+
17
+ displayLink.isPaused = false
18
+ }
19
+
20
+ @objc func removeKeyboardWatcher() {
21
+ displayLink.isPaused = true
22
+ }
23
+
24
+ @objc func updateKeyboardFrame(link: CADisplayLink) {
25
+ if keyboardTrackingView.view == nil {
26
+ return
27
+ }
28
+
29
+ let (visibleKeyboardHeight, keyboardFrameY) = keyboardTrackingView.view.frameTransitionInWindow
30
+ var keyboardPosition = visibleKeyboardHeight - KeyboardAreaExtender.shared.offset
31
+
32
+ if keyboardPosition == prevKeyboardPosition || keyboardFrameY == 0 {
33
+ return
34
+ }
35
+
36
+ if animation == nil {
37
+ initializeAnimation(fromValue: prevKeyboardPosition, toValue: keyboardHeight)
38
+ }
39
+
40
+ prevKeyboardPosition = keyboardPosition
41
+
42
+ if let animation = animation {
43
+ let baseDuration = animation.timingAt(value: keyboardPosition)
44
+
45
+ #if targetEnvironment(simulator)
46
+ // on iOS simulator we can not use static interval
47
+ // (from my observation from frame to frame we may have different delays)
48
+ // so for now we use approximation - we add a difference as
49
+ // beginTime - keyboardEventTime (but only in 0..0.016 range)
50
+ // and it gives satisfactory results (better than static delays)
51
+ let duration = baseDuration + animation.diff
52
+ #else
53
+ // 2 frames because we read previous frame, but need to calculate the next frame
54
+ let duration = baseDuration + link.duration * 2
55
+ #endif
56
+
57
+ let position = CGFloat(animation.valueAt(time: duration))
58
+ // handles a case when final frame has final destination (i. e. 0 or 291)
59
+ // but CASpringAnimation can never get to this final destination
60
+ let race: (CGFloat, CGFloat) -> CGFloat = animation.isIncreasing ? max : min
61
+ keyboardPosition = race(position, keyboardPosition)
62
+ }
63
+
64
+ onEvent(
65
+ "onKeyboardMove",
66
+ keyboardPosition as NSNumber,
67
+ keyboardPosition / CGFloat(keyboardHeight) as NSNumber,
68
+ duration as NSNumber,
69
+ tag
70
+ )
71
+ }
72
+ }
@@ -0,0 +1,63 @@
1
+ //
2
+ // KeyboardMovementObserver.swift
3
+ // KeyboardController
4
+ //
5
+ // Created by Kiryl Ziusko on 2.08.22.
6
+ // Copyright © 2022 Facebook. All rights reserved.
7
+ //
8
+
9
+ import Foundation
10
+ import UIKit
11
+
12
+ @objc(KeyboardMovementObserver)
13
+ public class KeyboardMovementObserver: NSObject {
14
+ // class members
15
+ var onEvent: (NSString, NSNumber, NSNumber, NSNumber, NSNumber) -> Void
16
+ var onNotify: (String, Any) -> Void
17
+ // animation
18
+ var onRequestAnimation: () -> Void
19
+ var onCancelAnimation: () -> Void
20
+ // progress tracker
21
+ var keyboardTrackingView = KeyboardTrackingView()
22
+ var animation: KeyboardAnimation?
23
+
24
+ var prevKeyboardPosition = 0.0
25
+ var displayLink: CADisplayLink!
26
+ var interactiveKeyboardObserver: NSKeyValueObservation?
27
+ var isMounted = false
28
+ // state variables
29
+ private var _keyboardHeight: CGFloat = 0.0
30
+ var keyboardHeight: CGFloat {
31
+ get { _keyboardHeight - KeyboardAreaExtender.shared.offset }
32
+ set { _keyboardHeight = newValue }
33
+ }
34
+
35
+ var duration = 0
36
+ var tag: NSNumber = -1
37
+ var didShowDeadline: Int64 = 0
38
+
39
+ @objc public init(
40
+ handler: @escaping (NSString, NSNumber, NSNumber, NSNumber, NSNumber) -> Void,
41
+ onNotify: @escaping (String, Any) -> Void,
42
+ onRequestAnimation: @escaping () -> Void,
43
+ onCancelAnimation: @escaping () -> Void
44
+ ) {
45
+ onEvent = handler
46
+ self.onNotify = onNotify
47
+ self.onRequestAnimation = onRequestAnimation
48
+ self.onCancelAnimation = onCancelAnimation
49
+
50
+ super.init()
51
+
52
+ displayLink = CADisplayLink(target: self, selector: #selector(updateKeyboardFrame))
53
+ displayLink.preferredFramesPerSecond = 120 // will fallback to 60 fps for devices without Pro Motion display
54
+ displayLink.add(to: .main, forMode: .common)
55
+ displayLink.isPaused = true
56
+ }
57
+
58
+ deinit {
59
+ displayLink.invalidate()
60
+ displayLink = nil
61
+ NotificationCenter.default.removeObserver(self)
62
+ }
63
+ }
@@ -178,16 +178,16 @@ using namespace facebook::react;
178
178
  .duration = [duration intValue],
179
179
  .target = [target intValue]});
180
180
  }
181
- }
182
- if ([event isEqualToString:@"onKeyboardMoveInteractive"]) {
183
- std::dynamic_pointer_cast<const facebook::react::KeyboardControllerViewEventEmitter>(
184
- self->_eventEmitter)
185
- ->onKeyboardMoveInteractive(
186
- facebook::react::KeyboardControllerViewEventEmitter::OnKeyboardMoveInteractive{
187
- .height = [height doubleValue],
188
- .progress = [progress doubleValue],
189
- .duration = [duration intValue],
190
- .target = [target intValue]});
181
+ if ([event isEqualToString:@"onKeyboardMoveInteractive"]) {
182
+ std::dynamic_pointer_cast<const facebook::react::KeyboardControllerViewEventEmitter>(
183
+ self->_eventEmitter)
184
+ ->onKeyboardMoveInteractive(facebook::react::KeyboardControllerViewEventEmitter::
185
+ OnKeyboardMoveInteractive{
186
+ .height = [height doubleValue],
187
+ .progress = [progress doubleValue],
188
+ .duration = [duration intValue],
189
+ .target = [target intValue]});
190
+ }
191
191
  }
192
192
 
193
193
  KeyboardMoveEvent *keyboardMoveEvent =