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
@@ -1,325 +0,0 @@
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
- private var _keyboardView: UIView?
22
- private var keyboardView: UIView? {
23
- let windowsCount = UIApplication.shared.windows.count
24
-
25
- if _keyboardView == nil || windowsCount != _windowsCount {
26
- _keyboardView = KeyboardView.find()
27
- _windowsCount = windowsCount
28
- }
29
-
30
- return _keyboardView
31
- }
32
-
33
- private var _windowsCount: Int = 0
34
- private var prevKeyboardPosition = 0.0
35
- private var displayLink: CADisplayLink!
36
- private var interactiveKeyboardObserver: NSKeyValueObservation?
37
- private var isMounted = false
38
- // state variables
39
- private var _keyboardHeight: CGFloat = 0.0
40
- private var keyboardHeight: CGFloat {
41
- get { _keyboardHeight - KeyboardAreaExtender.shared.offset }
42
- set { _keyboardHeight = newValue }
43
- }
44
-
45
- private var duration = 0
46
- private var tag: NSNumber = -1
47
- private var animation: KeyboardAnimation?
48
- private var didShowDeadline: Int64 = 0
49
-
50
- @objc public init(
51
- handler: @escaping (NSString, NSNumber, NSNumber, NSNumber, NSNumber) -> Void,
52
- onNotify: @escaping (String, Any) -> Void,
53
- onRequestAnimation: @escaping () -> Void,
54
- onCancelAnimation: @escaping () -> Void
55
- ) {
56
- onEvent = handler
57
- self.onNotify = onNotify
58
- self.onRequestAnimation = onRequestAnimation
59
- self.onCancelAnimation = onCancelAnimation
60
-
61
- super.init()
62
-
63
- displayLink = CADisplayLink(target: self, selector: #selector(updateKeyboardFrame))
64
- displayLink.preferredFramesPerSecond = 120 // will fallback to 60 fps for devices without Pro Motion display
65
- displayLink.add(to: .main, forMode: .common)
66
- displayLink.isPaused = true
67
- }
68
-
69
- deinit {
70
- displayLink.invalidate()
71
- displayLink = nil
72
- }
73
-
74
- @objc public func mount() {
75
- if isMounted {
76
- return
77
- }
78
-
79
- isMounted = true
80
-
81
- NotificationCenter.default.addObserver(
82
- self,
83
- selector: #selector(keyboardWillDisappear),
84
- name: UIResponder.keyboardWillHideNotification,
85
- object: nil
86
- )
87
- NotificationCenter.default.addObserver(
88
- self,
89
- selector: #selector(keyboardWillAppear),
90
- name: UIResponder.keyboardWillShowNotification,
91
- object: nil
92
- )
93
- NotificationCenter.default.addObserver(
94
- self,
95
- selector: #selector(keyboardDidAppear),
96
- name: UIResponder.keyboardDidShowNotification,
97
- object: nil
98
- )
99
- NotificationCenter.default.addObserver(
100
- self,
101
- selector: #selector(keyboardDidDisappear),
102
- name: UIResponder.keyboardDidHideNotification,
103
- object: nil
104
- )
105
- }
106
-
107
- private func setupKVObserver() {
108
- guard interactiveKeyboardObserver == nil, let view = keyboardView else { return }
109
-
110
- interactiveKeyboardObserver = view.observe(\.center, options: .new) { [weak self] _, change in
111
- guard let self = self, let changeValue = change.newValue else { return }
112
-
113
- self.keyboardDidMoveInteractively(changeValue: changeValue)
114
- }
115
- }
116
-
117
- private func removeKVObserver() {
118
- interactiveKeyboardObserver?.invalidate()
119
- interactiveKeyboardObserver = nil
120
- }
121
-
122
- private func keyboardDidMoveInteractively(changeValue: CGPoint) {
123
- if UIResponder.isKeyboardPreloading {
124
- return
125
- }
126
- // if we are currently animating keyboard -> we need to ignore values from KVO
127
- if !displayLink.isPaused {
128
- return
129
- }
130
- // if keyboard height is not equal to its bounds - we can ignore
131
- // values, since they'll be invalid and will cause UI jumps
132
- if floor(keyboardView?.bounds.size.height ?? 0) != floor(_keyboardHeight) {
133
- return
134
- }
135
-
136
- let keyboardFrameY = changeValue.y
137
- let keyboardWindowH = keyboardView?.window?.bounds.size.height ?? 0
138
- let keyboardPosition = keyboardWindowH - keyboardFrameY
139
-
140
- let position = CGFloat.interpolate(
141
- inputRange: [_keyboardHeight / 2, -_keyboardHeight / 2],
142
- outputRange: [_keyboardHeight, 0],
143
- currentValue: keyboardPosition
144
- ) - KeyboardAreaExtender.shared.offset
145
-
146
- if position == 0 {
147
- // it will be triggered before `keyboardWillDisappear` and
148
- // we don't need to trigger `onInteractive` handler for that
149
- // since it will be handled in `keyboardWillDisappear` function
150
- return
151
- }
152
-
153
- prevKeyboardPosition = position
154
-
155
- onEvent(
156
- "onKeyboardMoveInteractive",
157
- position as NSNumber,
158
- position / CGFloat(keyboardHeight) as NSNumber,
159
- -1,
160
- tag
161
- )
162
- }
163
-
164
- @objc public func unmount() {
165
- isMounted = false
166
- // swiftlint:disable:next notification_center_detachment
167
- NotificationCenter.default.removeObserver(self)
168
- }
169
-
170
- @objc func keyboardWillAppear(_ notification: Notification) {
171
- guard !KeyboardEventsIgnorer.shared.shouldIgnore, !UIResponder.isKeyboardPreloading else { return }
172
-
173
- let (duration, frame) = notification.keyboardMetaData()
174
- if let keyboardFrame = frame {
175
- tag = UIResponder.current.reactViewTag
176
- let keyboardHeight = keyboardFrame.cgRectValue.size.height
177
- self.keyboardHeight = keyboardHeight
178
- self.duration = duration
179
- didShowDeadline = Date.currentTimeStamp + Int64(duration)
180
-
181
- onRequestAnimation()
182
- onEvent("onKeyboardMoveStart", Float(self.keyboardHeight) as NSNumber, 1, duration as NSNumber, tag)
183
- onNotify("KeyboardController::keyboardWillShow", buildEventParams(self.keyboardHeight, duration, tag))
184
-
185
- setupKeyboardWatcher()
186
- initializeAnimation(fromValue: prevKeyboardPosition, toValue: self.keyboardHeight)
187
- }
188
- }
189
-
190
- @objc func keyboardWillDisappear(_ notification: Notification) {
191
- guard !UIResponder.isKeyboardPreloading else { return }
192
- let (duration, _) = notification.keyboardMetaData()
193
- tag = UIResponder.current.reactViewTag
194
- self.duration = duration
195
-
196
- onRequestAnimation()
197
- onEvent("onKeyboardMoveStart", 0, 0, duration as NSNumber, tag)
198
- onNotify("KeyboardController::keyboardWillHide", buildEventParams(0, duration, tag))
199
-
200
- setupKeyboardWatcher()
201
- removeKVObserver()
202
- initializeAnimation(fromValue: prevKeyboardPosition, toValue: 0)
203
- }
204
-
205
- @objc func keyboardDidAppear(_ notification: Notification) {
206
- guard !UIResponder.isKeyboardPreloading else { return }
207
- let timestamp = Date.currentTimeStamp
208
- let (duration, frame) = notification.keyboardMetaData()
209
- if let keyboardFrame = frame {
210
- let (position, _) = keyboardView.frameTransitionInWindow
211
- let keyboardHeight = keyboardFrame.cgRectValue.size.height
212
- tag = UIResponder.current.reactViewTag
213
- self.keyboardHeight = keyboardHeight
214
-
215
- guard !KeyboardEventsIgnorer.shared.shouldIgnore else {
216
- KeyboardEventsIgnorer.shared.shouldIgnoreKeyboardEvents = false
217
- return
218
- }
219
-
220
- // if the event is caught in between it's highly likely that it could be a "resize" event
221
- // so we just read actual keyboard frame value in this case
222
- let height = timestamp >= didShowDeadline ? self.keyboardHeight : position - KeyboardAreaExtender.shared.offset
223
- // always limit progress to the maximum possible value
224
- let progress = min(height / self.keyboardHeight, 1.0)
225
-
226
- onCancelAnimation()
227
- onEvent("onKeyboardMoveEnd", height as NSNumber, progress as NSNumber, duration as NSNumber, tag)
228
- onNotify("KeyboardController::keyboardDidShow", buildEventParams(height, duration, tag))
229
-
230
- removeKeyboardWatcher()
231
- setupKVObserver()
232
- animation = nil
233
- }
234
- }
235
-
236
- @objc func keyboardDidDisappear(_ notification: Notification) {
237
- guard !UIResponder.isKeyboardPreloading else { return }
238
- let (duration, _) = notification.keyboardMetaData()
239
- tag = UIResponder.current.reactViewTag
240
-
241
- onCancelAnimation()
242
- onEvent("onKeyboardMoveEnd", 0 as NSNumber, 0, duration as NSNumber, tag)
243
- onNotify("KeyboardController::keyboardDidHide", buildEventParams(0, duration, tag))
244
-
245
- removeKeyboardWatcher()
246
- animation = nil
247
- }
248
-
249
- @objc func setupKeyboardWatcher() {
250
- // sometimes `will` events can be called multiple times.
251
- // To avoid double re-creation of listener we are adding this condition
252
- // (if active link is present, then no need to re-setup a listener)
253
- if !displayLink.isPaused {
254
- return
255
- }
256
-
257
- displayLink.isPaused = false
258
- }
259
-
260
- @objc func removeKeyboardWatcher() {
261
- displayLink.isPaused = true
262
- }
263
-
264
- func initializeAnimation(fromValue: Double, toValue: Double) {
265
- for key in ["position", "opacity"] {
266
- if let keyboardAnimation = keyboardView?.layer.presentation()?.animation(forKey: key) {
267
- if let springAnimation = keyboardAnimation as? CASpringAnimation {
268
- animation = SpringAnimation(animation: springAnimation, fromValue: fromValue, toValue: toValue)
269
- } else if let basicAnimation = keyboardAnimation as? CABasicAnimation {
270
- animation = TimingAnimation(animation: basicAnimation, fromValue: fromValue, toValue: toValue)
271
- }
272
- return
273
- }
274
- }
275
- }
276
-
277
- @objc func updateKeyboardFrame(link: CADisplayLink) {
278
- if keyboardView == nil {
279
- return
280
- }
281
-
282
- let (visibleKeyboardHeight, keyboardFrameY) = keyboardView.frameTransitionInWindow
283
- var keyboardPosition = visibleKeyboardHeight - KeyboardAreaExtender.shared.offset
284
-
285
- if keyboardPosition == prevKeyboardPosition || keyboardFrameY == 0 {
286
- return
287
- }
288
-
289
- if animation == nil {
290
- initializeAnimation(fromValue: prevKeyboardPosition, toValue: keyboardHeight)
291
- }
292
-
293
- prevKeyboardPosition = keyboardPosition
294
-
295
- if let animation = animation {
296
- let baseDuration = animation.timingAt(value: keyboardPosition)
297
-
298
- #if targetEnvironment(simulator)
299
- // on iOS simulator we can not use static interval
300
- // (from my observation from frame to frame we may have different delays)
301
- // so for now we use approximation - we add a difference as
302
- // beginTime - keyboardEventTime (but only in 0..0.016 range)
303
- // and it gives satisfactory results (better than static delays)
304
- let duration = baseDuration + animation.diff
305
- #else
306
- // 2 frames because we read previous frame, but need to calculate the next frame
307
- let duration = baseDuration + link.duration * 2
308
- #endif
309
-
310
- let position = CGFloat(animation.valueAt(time: duration))
311
- // handles a case when final frame has final destination (i. e. 0 or 291)
312
- // but CASpringAnimation can never get to this final destination
313
- let race: (CGFloat, CGFloat) -> CGFloat = animation.isIncreasing ? max : min
314
- keyboardPosition = race(position, keyboardPosition)
315
- }
316
-
317
- onEvent(
318
- "onKeyboardMove",
319
- keyboardPosition as NSNumber,
320
- keyboardPosition / CGFloat(keyboardHeight) as NSNumber,
321
- duration as NSNumber,
322
- tag
323
- )
324
- }
325
- }