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.
- package/android/src/main/java/com/reactnativekeyboardcontroller/modules/statusbar/StatusBarManagerCompatModuleImpl.kt +2 -3
- package/android/src/main/java/com/reactnativekeyboardcontroller/views/EdgeToEdgeReactViewGroup.kt +12 -5
- package/ios/observers/movement/KeyboardTrackingView.swift +139 -0
- package/ios/observers/movement/KeyboardViewLocator.swift +23 -0
- package/ios/observers/movement/observer/KeyboardMovementObserver+Animation.swift +21 -0
- package/ios/observers/movement/observer/KeyboardMovementObserver+Interactive.swift +56 -0
- package/ios/observers/movement/observer/KeyboardMovementObserver+Lifecycle.swift +47 -0
- package/ios/observers/movement/observer/KeyboardMovementObserver+Listeners.swift +88 -0
- package/ios/observers/movement/observer/KeyboardMovementObserver+Watcher.swift +72 -0
- package/ios/observers/movement/observer/KeyboardMovementObserver.swift +63 -0
- package/ios/views/KeyboardControllerView.mm +10 -10
- package/ios/views/KeyboardExtenderContainerView.swift +119 -42
- package/ios/views/KeyboardExtenderManager.mm +12 -9
- package/lib/commonjs/components/KeyboardAwareScrollView/index.js +0 -1
- package/lib/commonjs/components/KeyboardAwareScrollView/index.js.map +1 -1
- package/lib/commonjs/types/views.js.map +1 -1
- package/lib/module/components/KeyboardAwareScrollView/index.js +0 -1
- package/lib/module/components/KeyboardAwareScrollView/index.js.map +1 -1
- package/lib/module/types/views.js.map +1 -1
- package/lib/typescript/types/views.d.ts +1 -1
- package/package.json +1 -1
- package/src/components/KeyboardAwareScrollView/index.tsx +0 -2
- package/src/types/views.ts +1 -1
- package/ios/observers/KeyboardMovementObserver.swift +0 -325
- /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
|
-
}
|
|
File without changes
|