react-native-keyboard-controller 1.8.0 → 1.9.1
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/README.md +2 -1
- package/android/gradle.properties +1 -1
- package/android/src/main/java/com/reactnativekeyboardcontroller/events/FocusedInputLayoutChangedEvent.kt +41 -0
- package/android/src/main/java/com/reactnativekeyboardcontroller/extensions/ThemedReactContext.kt +9 -0
- package/android/src/main/java/com/reactnativekeyboardcontroller/extensions/View.kt +7 -0
- package/android/src/main/java/com/reactnativekeyboardcontroller/listeners/FocusedInputLayoutObserver.kt +93 -0
- package/android/src/main/java/com/reactnativekeyboardcontroller/{KeyboardAnimationCallback.kt → listeners/KeyboardAnimationCallback.kt} +102 -63
- package/android/src/main/java/com/reactnativekeyboardcontroller/managers/KeyboardControllerViewManagerImpl.kt +2 -0
- package/android/src/main/java/com/reactnativekeyboardcontroller/views/EdgeToEdgeReactViewGroup.kt +16 -8
- package/ios/Extensions.swift +7 -0
- package/ios/KeyboardController.xcodeproj/project.pbxproj +41 -7
- package/ios/events/FocusedInputLayoutChangedEvent.h +16 -0
- package/ios/events/FocusedInputLayoutChangedEvent.m +75 -0
- package/ios/observers/FocusedInputLayoutObserver.swift +136 -0
- package/ios/{KeyboardControllerView.mm → views/KeyboardControllerView.mm} +40 -4
- package/ios/{KeyboardControllerViewManager.mm → views/KeyboardControllerViewManager.mm} +3 -0
- package/ios/{KeyboardControllerViewManager.swift → views/KeyboardControllerViewManager.swift} +14 -0
- package/jest/index.js +16 -0
- package/lib/commonjs/animated.js +18 -4
- package/lib/commonjs/animated.js.map +1 -1
- package/lib/commonjs/bindings.js.map +1 -1
- package/lib/commonjs/bindings.native.js.map +1 -1
- package/lib/commonjs/components/KeyboardAvoidingView/hooks.js.map +1 -1
- package/lib/commonjs/components/KeyboardAvoidingView/index.js.map +1 -1
- package/lib/commonjs/components/KeyboardStickyView/index.js +44 -0
- package/lib/commonjs/components/KeyboardStickyView/index.js.map +1 -0
- package/lib/commonjs/components/index.js +7 -0
- package/lib/commonjs/components/index.js.map +1 -1
- package/lib/commonjs/constants.js.map +1 -1
- package/lib/commonjs/context.js +6 -3
- package/lib/commonjs/context.js.map +1 -1
- package/lib/commonjs/hooks.js +8 -1
- package/lib/commonjs/hooks.js.map +1 -1
- package/lib/commonjs/index.js +8 -1
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/internal.js.map +1 -1
- package/lib/commonjs/reanimated.js +5 -2
- package/lib/commonjs/reanimated.js.map +1 -1
- package/lib/commonjs/reanimated.native.js +18 -1
- package/lib/commonjs/reanimated.native.js.map +1 -1
- package/lib/commonjs/replicas.js +1 -1
- package/lib/commonjs/replicas.js.map +1 -1
- package/lib/commonjs/specs/KeyboardControllerViewNativeComponent.js.map +1 -1
- package/lib/commonjs/specs/KeyboardGestureAreaNativeComponent.js.map +1 -1
- package/lib/commonjs/specs/NativeKeyboardController.js.map +1 -1
- package/lib/commonjs/specs/NativeStatusBarManagerCompat.js.map +1 -1
- package/lib/commonjs/types.js.map +1 -1
- package/lib/module/animated.js +19 -5
- package/lib/module/animated.js.map +1 -1
- package/lib/module/bindings.js.map +1 -1
- package/lib/module/bindings.native.js +1 -1
- package/lib/module/bindings.native.js.map +1 -1
- package/lib/module/components/KeyboardAvoidingView/hooks.js.map +1 -1
- package/lib/module/components/KeyboardAvoidingView/index.js +2 -2
- package/lib/module/components/KeyboardAvoidingView/index.js.map +1 -1
- package/lib/module/components/KeyboardStickyView/index.js +35 -0
- package/lib/module/components/KeyboardStickyView/index.js.map +1 -0
- package/lib/module/components/index.js +1 -0
- package/lib/module/components/index.js.map +1 -1
- package/lib/module/constants.js.map +1 -1
- package/lib/module/context.js +6 -3
- package/lib/module/context.js.map +1 -1
- package/lib/module/hooks.js +7 -1
- package/lib/module/hooks.js.map +1 -1
- package/lib/module/index.js +1 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/internal.js.map +1 -1
- package/lib/module/reanimated.js +3 -1
- package/lib/module/reanimated.js.map +1 -1
- package/lib/module/reanimated.native.js +16 -0
- package/lib/module/reanimated.native.js.map +1 -1
- package/lib/module/replicas.js +2 -2
- package/lib/module/replicas.js.map +1 -1
- package/lib/module/specs/KeyboardControllerViewNativeComponent.js.map +1 -1
- package/lib/module/specs/KeyboardGestureAreaNativeComponent.js.map +1 -1
- package/lib/module/specs/NativeKeyboardController.js.map +1 -1
- package/lib/module/specs/NativeStatusBarManagerCompat.js.map +1 -1
- package/lib/module/types.js.map +1 -1
- package/lib/typescript/components/KeyboardAvoidingView/index.d.ts +2 -1
- package/lib/typescript/components/KeyboardStickyView/index.d.ts +20 -0
- package/lib/typescript/components/index.d.ts +1 -0
- package/lib/typescript/context.d.ts +3 -2
- package/lib/typescript/hooks.d.ts +5 -2
- package/lib/typescript/index.d.ts +1 -1
- package/lib/typescript/internal.d.ts +1 -1
- package/lib/typescript/reanimated.d.ts +2 -1
- package/lib/typescript/reanimated.native.d.ts +2 -1
- package/lib/typescript/specs/KeyboardControllerViewNativeComponent.d.ts +12 -0
- package/lib/typescript/types.d.ts +16 -0
- package/package.json +9 -28
- package/src/animated.tsx +30 -7
- package/src/bindings.native.ts +1 -1
- package/src/bindings.ts +3 -1
- package/src/components/KeyboardAvoidingView/hooks.ts +1 -0
- package/src/components/KeyboardAvoidingView/index.tsx +8 -10
- package/src/components/KeyboardStickyView/index.tsx +63 -0
- package/src/components/index.ts +1 -0
- package/src/constants.ts +2 -0
- package/src/context.ts +12 -5
- package/src/hooks.ts +10 -6
- package/src/index.ts +1 -1
- package/src/internal.ts +3 -1
- package/src/reanimated.native.ts +30 -1
- package/src/reanimated.ts +13 -2
- package/src/replicas.ts +2 -2
- package/src/specs/KeyboardControllerViewNativeComponent.ts +15 -1
- package/src/specs/KeyboardGestureAreaNativeComponent.ts +2 -1
- package/src/specs/NativeKeyboardController.ts +2 -1
- package/src/specs/NativeStatusBarManagerCompat.ts +2 -1
- package/src/types.ts +27 -1
- /package/ios/{KeyboardMoveEvent.h → events/KeyboardMoveEvent.h} +0 -0
- /package/ios/{KeyboardMoveEvent.m → events/KeyboardMoveEvent.m} +0 -0
- /package/ios/{KeyboardMovementObserver.swift → observers/KeyboardMovementObserver.swift} +0 -0
- /package/ios/{KeyboardControllerView.h → views/KeyboardControllerView.h} +0 -0
package/README.md
CHANGED
|
@@ -13,7 +13,8 @@ Keyboard manager which works in identical way on both iOS and Android.
|
|
|
13
13
|
- module for changing soft input mode on Android 🤔
|
|
14
14
|
- reanimated support 🚀
|
|
15
15
|
- interactive keyboard dismissing 👆📱
|
|
16
|
-
-
|
|
16
|
+
- prebuilt components (`KeyboardStickyView`, re-worked `KeyboardAvoidingView`) 📚
|
|
17
|
+
- easy focused input information retrieval 📝 🔮
|
|
17
18
|
- works with any navigation library 🧭
|
|
18
19
|
- and more is coming... Stay tuned! 😊
|
|
19
20
|
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
package com.reactnativekeyboardcontroller.events
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.bridge.Arguments
|
|
4
|
+
import com.facebook.react.bridge.WritableMap
|
|
5
|
+
import com.facebook.react.uimanager.events.Event
|
|
6
|
+
|
|
7
|
+
data class FocusedInputLayoutChangedEventData(
|
|
8
|
+
val x: Double,
|
|
9
|
+
val y: Double,
|
|
10
|
+
val width: Double,
|
|
11
|
+
val height: Double,
|
|
12
|
+
val absoluteX: Double,
|
|
13
|
+
val absoluteY: Double,
|
|
14
|
+
val target: Int,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
class FocusedInputLayoutChangedEvent(
|
|
18
|
+
surfaceId: Int,
|
|
19
|
+
viewId: Int,
|
|
20
|
+
private val event: FocusedInputLayoutChangedEventData,
|
|
21
|
+
) : Event<KeyboardTransitionEvent>(surfaceId, viewId) {
|
|
22
|
+
override fun getEventName() = "topFocusedInputLayoutChanged"
|
|
23
|
+
|
|
24
|
+
// All events for a given view can be coalesced
|
|
25
|
+
override fun getCoalescingKey(): Short = 0
|
|
26
|
+
|
|
27
|
+
override fun getEventData(): WritableMap? = Arguments.createMap().apply {
|
|
28
|
+
putInt("target", event.target)
|
|
29
|
+
putMap(
|
|
30
|
+
"layout",
|
|
31
|
+
Arguments.createMap().apply {
|
|
32
|
+
putDouble("x", event.x)
|
|
33
|
+
putDouble("y", event.y)
|
|
34
|
+
putDouble("width", event.width)
|
|
35
|
+
putDouble("height", event.height)
|
|
36
|
+
putDouble("absoluteX", event.absoluteX)
|
|
37
|
+
putDouble("absoluteY", event.absoluteY)
|
|
38
|
+
},
|
|
39
|
+
)
|
|
40
|
+
}
|
|
41
|
+
}
|
package/android/src/main/java/com/reactnativekeyboardcontroller/extensions/ThemedReactContext.kt
CHANGED
|
@@ -2,6 +2,15 @@ package com.reactnativekeyboardcontroller.extensions
|
|
|
2
2
|
|
|
3
3
|
import android.view.View
|
|
4
4
|
import com.facebook.react.uimanager.ThemedReactContext
|
|
5
|
+
import com.facebook.react.uimanager.UIManagerHelper
|
|
6
|
+
import com.facebook.react.uimanager.events.Event
|
|
7
|
+
import com.facebook.react.uimanager.events.EventDispatcher
|
|
5
8
|
|
|
6
9
|
val ThemedReactContext.rootView: View?
|
|
7
10
|
get() = this.currentActivity?.window?.decorView?.rootView
|
|
11
|
+
|
|
12
|
+
fun ThemedReactContext?.dispatchEvent(viewId: Int, event: Event<*>) {
|
|
13
|
+
val eventDispatcher: EventDispatcher? =
|
|
14
|
+
UIManagerHelper.getEventDispatcherForReactTag(this, viewId)
|
|
15
|
+
eventDispatcher?.dispatchEvent(event)
|
|
16
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
package com.reactnativekeyboardcontroller.listeners
|
|
2
|
+
|
|
3
|
+
import android.view.View.OnLayoutChangeListener
|
|
4
|
+
import android.view.ViewTreeObserver.OnGlobalFocusChangeListener
|
|
5
|
+
import com.facebook.react.uimanager.ThemedReactContext
|
|
6
|
+
import com.facebook.react.uimanager.UIManagerHelper
|
|
7
|
+
import com.facebook.react.views.textinput.ReactEditText
|
|
8
|
+
import com.facebook.react.views.view.ReactViewGroup
|
|
9
|
+
import com.reactnativekeyboardcontroller.events.FocusedInputLayoutChangedEvent
|
|
10
|
+
import com.reactnativekeyboardcontroller.events.FocusedInputLayoutChangedEventData
|
|
11
|
+
import com.reactnativekeyboardcontroller.extensions.dispatchEvent
|
|
12
|
+
import com.reactnativekeyboardcontroller.extensions.dp
|
|
13
|
+
import com.reactnativekeyboardcontroller.extensions.screenLocation
|
|
14
|
+
|
|
15
|
+
val noFocusedInputEvent = FocusedInputLayoutChangedEventData(
|
|
16
|
+
x = 0.0,
|
|
17
|
+
y = 0.0,
|
|
18
|
+
width = 0.0,
|
|
19
|
+
height = 0.0,
|
|
20
|
+
absoluteX = 0.0,
|
|
21
|
+
absoluteY = 0.0,
|
|
22
|
+
target = -1,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
class FocusedInputLayoutObserver(val view: ReactViewGroup, private val context: ThemedReactContext?) {
|
|
26
|
+
// constructor variables
|
|
27
|
+
private val surfaceId = UIManagerHelper.getSurfaceId(view)
|
|
28
|
+
|
|
29
|
+
// state variables
|
|
30
|
+
private var lastFocusedInput: ReactEditText? = null
|
|
31
|
+
private var lastEventDispatched: FocusedInputLayoutChangedEventData = noFocusedInputEvent
|
|
32
|
+
|
|
33
|
+
// listeners
|
|
34
|
+
private val layoutListener =
|
|
35
|
+
OnLayoutChangeListener { v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom ->
|
|
36
|
+
this.syncUpLayout()
|
|
37
|
+
}
|
|
38
|
+
private val focusListener = OnGlobalFocusChangeListener { oldFocus, newFocus ->
|
|
39
|
+
// unfocused or focused was changed
|
|
40
|
+
if (newFocus == null || oldFocus != null) {
|
|
41
|
+
lastFocusedInput?.removeOnLayoutChangeListener(layoutListener)
|
|
42
|
+
lastFocusedInput = null
|
|
43
|
+
}
|
|
44
|
+
if (newFocus is ReactEditText) {
|
|
45
|
+
lastFocusedInput = newFocus
|
|
46
|
+
newFocus.addOnLayoutChangeListener(layoutListener)
|
|
47
|
+
this.syncUpLayout()
|
|
48
|
+
}
|
|
49
|
+
// unfocused
|
|
50
|
+
if (newFocus == null) {
|
|
51
|
+
dispatchEventToJS(noFocusedInputEvent)
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
init {
|
|
56
|
+
view.viewTreeObserver.addOnGlobalFocusChangeListener(focusListener)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
fun syncUpLayout() {
|
|
60
|
+
val input = lastFocusedInput ?: return
|
|
61
|
+
|
|
62
|
+
val (x, y) = input.screenLocation
|
|
63
|
+
val event = FocusedInputLayoutChangedEventData(
|
|
64
|
+
x = input.x.dp,
|
|
65
|
+
y = input.y.dp,
|
|
66
|
+
width = input.width.toFloat().dp,
|
|
67
|
+
height = input.height.toFloat().dp,
|
|
68
|
+
absoluteX = x.toFloat().dp,
|
|
69
|
+
absoluteY = y.toFloat().dp,
|
|
70
|
+
target = input.id,
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
dispatchEventToJS(event)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
fun destroy() {
|
|
77
|
+
view.viewTreeObserver.removeOnGlobalFocusChangeListener(focusListener)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
private fun dispatchEventToJS(event: FocusedInputLayoutChangedEventData) {
|
|
81
|
+
if (event != lastEventDispatched) {
|
|
82
|
+
lastEventDispatched = event
|
|
83
|
+
context.dispatchEvent(
|
|
84
|
+
view.id,
|
|
85
|
+
FocusedInputLayoutChangedEvent(
|
|
86
|
+
surfaceId,
|
|
87
|
+
view.id,
|
|
88
|
+
event = event,
|
|
89
|
+
),
|
|
90
|
+
)
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
package com.reactnativekeyboardcontroller
|
|
1
|
+
package com.reactnativekeyboardcontroller.listeners
|
|
2
2
|
|
|
3
3
|
import android.animation.ValueAnimator
|
|
4
4
|
import android.os.Build
|
|
5
5
|
import android.util.Log
|
|
6
6
|
import android.view.View
|
|
7
|
+
import android.view.ViewTreeObserver.OnGlobalFocusChangeListener
|
|
7
8
|
import androidx.core.animation.doOnEnd
|
|
8
9
|
import androidx.core.graphics.Insets
|
|
9
10
|
import androidx.core.view.OnApplyWindowInsetsListener
|
|
@@ -15,11 +16,11 @@ import com.facebook.react.bridge.WritableMap
|
|
|
15
16
|
import com.facebook.react.modules.core.DeviceEventManagerModule
|
|
16
17
|
import com.facebook.react.uimanager.ThemedReactContext
|
|
17
18
|
import com.facebook.react.uimanager.UIManagerHelper
|
|
18
|
-
import com.facebook.react.uimanager.events.Event
|
|
19
|
-
import com.facebook.react.uimanager.events.EventDispatcher
|
|
20
19
|
import com.facebook.react.views.textinput.ReactEditText
|
|
21
20
|
import com.facebook.react.views.view.ReactViewGroup
|
|
21
|
+
import com.reactnativekeyboardcontroller.InteractiveKeyboardProvider
|
|
22
22
|
import com.reactnativekeyboardcontroller.events.KeyboardTransitionEvent
|
|
23
|
+
import com.reactnativekeyboardcontroller.extensions.dispatchEvent
|
|
23
24
|
import com.reactnativekeyboardcontroller.extensions.dp
|
|
24
25
|
import kotlin.math.abs
|
|
25
26
|
|
|
@@ -33,56 +34,65 @@ class KeyboardAnimationCallback(
|
|
|
33
34
|
val context: ThemedReactContext?,
|
|
34
35
|
) : WindowInsetsAnimationCompat.Callback(dispatchMode), OnApplyWindowInsetsListener {
|
|
35
36
|
private val surfaceId = UIManagerHelper.getSurfaceId(view)
|
|
37
|
+
|
|
38
|
+
// state variables
|
|
36
39
|
private var persistentKeyboardHeight = 0.0
|
|
37
40
|
private var isKeyboardVisible = false
|
|
38
41
|
private var isTransitioning = false
|
|
39
42
|
private var duration = 0
|
|
40
43
|
private var viewTagFocused = -1
|
|
41
44
|
|
|
45
|
+
// listeners
|
|
46
|
+
private val focusListener = OnGlobalFocusChangeListener { oldFocus, newFocus ->
|
|
47
|
+
if (newFocus is ReactEditText) {
|
|
48
|
+
viewTagFocused = newFocus.id
|
|
49
|
+
|
|
50
|
+
// keyboard is visible and focus has been changed
|
|
51
|
+
if (this.isKeyboardVisible && oldFocus !== null) {
|
|
52
|
+
// imitate iOS behavior and send two instant start/end events containing an info about new tag
|
|
53
|
+
// 1. onStart/onMove/onEnd can be still dispatched after, if keyboard change size (numeric -> alphabetic type)
|
|
54
|
+
// 2. event should be send only when keyboard is visible, since this event arrives earlier -> `tag` will be
|
|
55
|
+
// 100% included in onStart/onMove/onEnd lifecycles, but triggering onStart/onEnd several time
|
|
56
|
+
// can bring breaking changes
|
|
57
|
+
context.dispatchEvent(
|
|
58
|
+
view.id,
|
|
59
|
+
KeyboardTransitionEvent(
|
|
60
|
+
surfaceId,
|
|
61
|
+
view.id,
|
|
62
|
+
"topKeyboardMoveStart",
|
|
63
|
+
this.persistentKeyboardHeight,
|
|
64
|
+
1.0,
|
|
65
|
+
0,
|
|
66
|
+
viewTagFocused,
|
|
67
|
+
),
|
|
68
|
+
)
|
|
69
|
+
context.dispatchEvent(
|
|
70
|
+
view.id,
|
|
71
|
+
KeyboardTransitionEvent(
|
|
72
|
+
surfaceId,
|
|
73
|
+
view.id,
|
|
74
|
+
"topKeyboardMoveEnd",
|
|
75
|
+
this.persistentKeyboardHeight,
|
|
76
|
+
1.0,
|
|
77
|
+
0,
|
|
78
|
+
viewTagFocused,
|
|
79
|
+
),
|
|
80
|
+
)
|
|
81
|
+
this.emitEvent("KeyboardController::keyboardWillShow", getEventParams(this.persistentKeyboardHeight))
|
|
82
|
+
this.emitEvent("KeyboardController::keyboardDidShow", getEventParams(this.persistentKeyboardHeight))
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
private var layoutObserver: FocusedInputLayoutObserver? = null
|
|
87
|
+
|
|
42
88
|
init {
|
|
43
89
|
require(persistentInsetTypes and deferredInsetTypes == 0) {
|
|
44
90
|
"persistentInsetTypes and deferredInsetTypes can not contain any of " +
|
|
45
91
|
" same WindowInsetsCompat.Type values"
|
|
46
92
|
}
|
|
47
93
|
|
|
48
|
-
view
|
|
49
|
-
|
|
50
|
-
viewTagFocused = newFocus.id
|
|
51
|
-
|
|
52
|
-
// keyboard is visible and focus has been changed
|
|
53
|
-
if (this.isKeyboardVisible && oldFocus !== null) {
|
|
54
|
-
// imitate iOS behavior and send two instant start/end events containing an info about new tag
|
|
55
|
-
// 1. onStart/onMove/onEnd can be still dispatched after, if keyboard change size (numeric -> alphabetic type)
|
|
56
|
-
// 2. event should be send only when keyboard is visible, since this event arrives earlier -> `tag` will be
|
|
57
|
-
// 100% included in onStart/onMove/onEnd lifecycles, but triggering onStart/onEnd several time
|
|
58
|
-
// can bring breaking changes
|
|
59
|
-
this.sendEventToJS(
|
|
60
|
-
KeyboardTransitionEvent(
|
|
61
|
-
surfaceId,
|
|
62
|
-
view.id,
|
|
63
|
-
"topKeyboardMoveStart",
|
|
64
|
-
this.persistentKeyboardHeight,
|
|
65
|
-
1.0,
|
|
66
|
-
0,
|
|
67
|
-
viewTagFocused,
|
|
68
|
-
),
|
|
69
|
-
)
|
|
70
|
-
this.sendEventToJS(
|
|
71
|
-
KeyboardTransitionEvent(
|
|
72
|
-
surfaceId,
|
|
73
|
-
view.id,
|
|
74
|
-
"topKeyboardMoveEnd",
|
|
75
|
-
this.persistentKeyboardHeight,
|
|
76
|
-
1.0,
|
|
77
|
-
0,
|
|
78
|
-
viewTagFocused,
|
|
79
|
-
),
|
|
80
|
-
)
|
|
81
|
-
this.emitEvent("KeyboardController::keyboardWillShow", getEventParams(this.persistentKeyboardHeight))
|
|
82
|
-
this.emitEvent("KeyboardController::keyboardDidShow", getEventParams(this.persistentKeyboardHeight))
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
}
|
|
94
|
+
layoutObserver = FocusedInputLayoutObserver(view = view, context = context)
|
|
95
|
+
view.viewTreeObserver.addOnGlobalFocusChangeListener(focusListener)
|
|
86
96
|
}
|
|
87
97
|
|
|
88
98
|
/**
|
|
@@ -100,6 +110,7 @@ class KeyboardAnimationCallback(
|
|
|
100
110
|
* - we dispatch `keyboardDidShow` (onEnd).
|
|
101
111
|
*/
|
|
102
112
|
override fun onApplyWindowInsets(v: View, insets: WindowInsetsCompat): WindowInsetsCompat {
|
|
113
|
+
val keyboardHeight = getCurrentKeyboardHeight()
|
|
103
114
|
// when keyboard appears values will be (false && true)
|
|
104
115
|
// when keyboard disappears values will be (true && false)
|
|
105
116
|
val isKeyboardShown = isKeyboardVisible && isKeyboardVisible()
|
|
@@ -111,53 +122,65 @@ class KeyboardAnimationCallback(
|
|
|
111
122
|
// `InteractiveKeyboardProvider.isInteractive` detect case when keyboard moves
|
|
112
123
|
// because of the gesture
|
|
113
124
|
val isMoving = isTransitioning || InteractiveKeyboardProvider.isInteractive
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
125
|
+
val isKeyboardFullyVisible = isKeyboardShown && !isMoving
|
|
126
|
+
// when keyboard is opened and we trigger a transition from screen A to screen B,
|
|
127
|
+
// then this method is getting called and we start dispatching events, and later original
|
|
128
|
+
// `onStart`/`onProgress`/`onEnd` emitting their events (since keyboard is closing) and we
|
|
129
|
+
// are getting race conditions.
|
|
130
|
+
//
|
|
131
|
+
// but in general this check is a must because we are detecting keyboard size changes
|
|
132
|
+
// in this method
|
|
133
|
+
val isKeyboardSizeEqual = this.persistentKeyboardHeight == keyboardHeight
|
|
117
134
|
|
|
135
|
+
if (isKeyboardFullyVisible && !isKeyboardSizeEqual && Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
|
136
|
+
layoutObserver?.syncUpLayout()
|
|
118
137
|
this.emitEvent("KeyboardController::keyboardWillShow", getEventParams(keyboardHeight))
|
|
119
|
-
|
|
138
|
+
context.dispatchEvent(
|
|
139
|
+
view.id,
|
|
120
140
|
KeyboardTransitionEvent(
|
|
121
141
|
surfaceId,
|
|
122
142
|
view.id,
|
|
123
143
|
"topKeyboardMoveStart",
|
|
124
144
|
keyboardHeight,
|
|
125
145
|
1.0,
|
|
126
|
-
|
|
146
|
+
DEFAULT_ANIMATION_TIME,
|
|
127
147
|
viewTagFocused,
|
|
128
148
|
),
|
|
129
149
|
)
|
|
130
150
|
|
|
131
|
-
val animation =
|
|
151
|
+
val animation =
|
|
152
|
+
ValueAnimator.ofFloat(this.persistentKeyboardHeight.toFloat(), keyboardHeight.toFloat())
|
|
132
153
|
animation.addUpdateListener { animator ->
|
|
133
154
|
val toValue = animator.animatedValue as Float
|
|
134
|
-
|
|
155
|
+
context.dispatchEvent(
|
|
156
|
+
view.id,
|
|
135
157
|
KeyboardTransitionEvent(
|
|
136
158
|
surfaceId,
|
|
137
159
|
view.id,
|
|
138
160
|
"topKeyboardMove",
|
|
139
161
|
toValue.toDouble(),
|
|
140
162
|
toValue.toDouble() / keyboardHeight,
|
|
141
|
-
|
|
163
|
+
DEFAULT_ANIMATION_TIME,
|
|
142
164
|
viewTagFocused,
|
|
143
165
|
),
|
|
144
166
|
)
|
|
145
167
|
}
|
|
146
168
|
animation.doOnEnd {
|
|
147
169
|
this.emitEvent("KeyboardController::keyboardDidShow", getEventParams(keyboardHeight))
|
|
148
|
-
|
|
170
|
+
context.dispatchEvent(
|
|
171
|
+
view.id,
|
|
149
172
|
KeyboardTransitionEvent(
|
|
150
173
|
surfaceId,
|
|
151
174
|
view.id,
|
|
152
175
|
"topKeyboardMoveEnd",
|
|
153
176
|
keyboardHeight,
|
|
154
177
|
1.0,
|
|
155
|
-
|
|
178
|
+
DEFAULT_ANIMATION_TIME,
|
|
156
179
|
viewTagFocused,
|
|
157
180
|
),
|
|
158
181
|
)
|
|
159
182
|
}
|
|
160
|
-
animation.setDuration(DEFAULT_ANIMATION_TIME).startDelay = 0
|
|
183
|
+
animation.setDuration(DEFAULT_ANIMATION_TIME.toLong()).startDelay = 0
|
|
161
184
|
animation.start()
|
|
162
185
|
|
|
163
186
|
this.persistentKeyboardHeight = keyboardHeight
|
|
@@ -180,13 +203,15 @@ class KeyboardAnimationCallback(
|
|
|
180
203
|
this.persistentKeyboardHeight = keyboardHeight
|
|
181
204
|
}
|
|
182
205
|
|
|
206
|
+
layoutObserver?.syncUpLayout()
|
|
183
207
|
this.emitEvent(
|
|
184
208
|
"KeyboardController::" + if (!isKeyboardVisible) "keyboardWillHide" else "keyboardWillShow",
|
|
185
209
|
getEventParams(keyboardHeight),
|
|
186
210
|
)
|
|
187
211
|
|
|
188
212
|
Log.i(TAG, "HEIGHT:: $keyboardHeight TAG:: $viewTagFocused")
|
|
189
|
-
|
|
213
|
+
context.dispatchEvent(
|
|
214
|
+
view.id,
|
|
190
215
|
KeyboardTransitionEvent(
|
|
191
216
|
surfaceId,
|
|
192
217
|
view.id,
|
|
@@ -227,10 +252,24 @@ class KeyboardAnimationCallback(
|
|
|
227
252
|
// do nothing, just log an exception send progress as 0
|
|
228
253
|
Log.w(TAG, "Caught arithmetic exception during `progress` calculation: $e")
|
|
229
254
|
}
|
|
230
|
-
Log.i(
|
|
255
|
+
Log.i(
|
|
256
|
+
TAG,
|
|
257
|
+
"DiffY: $diffY $height $progress ${InteractiveKeyboardProvider.isInteractive} $viewTagFocused",
|
|
258
|
+
)
|
|
231
259
|
|
|
232
260
|
val event = if (InteractiveKeyboardProvider.isInteractive) "topKeyboardMoveInteractive" else "topKeyboardMove"
|
|
233
|
-
|
|
261
|
+
context.dispatchEvent(
|
|
262
|
+
view.id,
|
|
263
|
+
KeyboardTransitionEvent(
|
|
264
|
+
surfaceId,
|
|
265
|
+
view.id,
|
|
266
|
+
event,
|
|
267
|
+
height,
|
|
268
|
+
progress,
|
|
269
|
+
duration,
|
|
270
|
+
viewTagFocused,
|
|
271
|
+
),
|
|
272
|
+
)
|
|
234
273
|
|
|
235
274
|
return insets
|
|
236
275
|
}
|
|
@@ -259,7 +298,8 @@ class KeyboardAnimationCallback(
|
|
|
259
298
|
"KeyboardController::" + if (!isKeyboardVisible) "keyboardDidHide" else "keyboardDidShow",
|
|
260
299
|
getEventParams(keyboardHeight),
|
|
261
300
|
)
|
|
262
|
-
|
|
301
|
+
context.dispatchEvent(
|
|
302
|
+
view.id,
|
|
263
303
|
KeyboardTransitionEvent(
|
|
264
304
|
surfaceId,
|
|
265
305
|
view.id,
|
|
@@ -275,6 +315,11 @@ class KeyboardAnimationCallback(
|
|
|
275
315
|
duration = 0
|
|
276
316
|
}
|
|
277
317
|
|
|
318
|
+
fun destroy() {
|
|
319
|
+
view.viewTreeObserver.removeOnGlobalFocusChangeListener(focusListener)
|
|
320
|
+
layoutObserver?.destroy()
|
|
321
|
+
}
|
|
322
|
+
|
|
278
323
|
private fun isKeyboardVisible(): Boolean {
|
|
279
324
|
val insets = ViewCompat.getRootWindowInsets(view)
|
|
280
325
|
|
|
@@ -290,12 +335,6 @@ class KeyboardAnimationCallback(
|
|
|
290
335
|
return (keyboardHeight - navigationBar).toFloat().dp.coerceAtLeast(0.0)
|
|
291
336
|
}
|
|
292
337
|
|
|
293
|
-
private fun sendEventToJS(event: Event<*>) {
|
|
294
|
-
val eventDispatcher: EventDispatcher? =
|
|
295
|
-
UIManagerHelper.getEventDispatcherForReactTag(context, view.id)
|
|
296
|
-
eventDispatcher?.dispatchEvent(event)
|
|
297
|
-
}
|
|
298
|
-
|
|
299
338
|
private fun emitEvent(event: String, params: WritableMap) {
|
|
300
339
|
context?.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)?.emit(event, params)
|
|
301
340
|
|
|
@@ -313,6 +352,6 @@ class KeyboardAnimationCallback(
|
|
|
313
352
|
}
|
|
314
353
|
|
|
315
354
|
companion object {
|
|
316
|
-
private const val DEFAULT_ANIMATION_TIME =
|
|
355
|
+
private const val DEFAULT_ANIMATION_TIME = 250
|
|
317
356
|
}
|
|
318
357
|
}
|
|
@@ -33,6 +33,8 @@ class KeyboardControllerViewManagerImpl(mReactContext: ReactApplicationContext)
|
|
|
33
33
|
MapBuilder.of("registrationName", "onKeyboardMoveEnd"),
|
|
34
34
|
"topKeyboardMoveInteractive",
|
|
35
35
|
MapBuilder.of("registrationName", "onKeyboardMoveInteractive"),
|
|
36
|
+
"topFocusedInputLayoutChanged",
|
|
37
|
+
MapBuilder.of("registrationName", "onFocusedInputLayoutChanged"),
|
|
36
38
|
)
|
|
37
39
|
|
|
38
40
|
return map
|
package/android/src/main/java/com/reactnativekeyboardcontroller/views/EdgeToEdgeReactViewGroup.kt
CHANGED
|
@@ -12,10 +12,10 @@ import androidx.core.view.WindowInsetsAnimationCompat
|
|
|
12
12
|
import androidx.core.view.WindowInsetsCompat
|
|
13
13
|
import com.facebook.react.uimanager.ThemedReactContext
|
|
14
14
|
import com.facebook.react.views.view.ReactViewGroup
|
|
15
|
-
import com.reactnativekeyboardcontroller.KeyboardAnimationCallback
|
|
16
15
|
import com.reactnativekeyboardcontroller.extensions.removeSelf
|
|
17
16
|
import com.reactnativekeyboardcontroller.extensions.requestApplyInsetsWhenAttached
|
|
18
17
|
import com.reactnativekeyboardcontroller.extensions.rootView
|
|
18
|
+
import com.reactnativekeyboardcontroller.listeners.KeyboardAnimationCallback
|
|
19
19
|
|
|
20
20
|
private val TAG = EdgeToEdgeReactViewGroup::class.qualifiedName
|
|
21
21
|
|
|
@@ -30,6 +30,7 @@ class EdgeToEdgeReactViewGroup(private val reactContext: ThemedReactContext) : R
|
|
|
30
30
|
// internal class members
|
|
31
31
|
private var eventView: ReactViewGroup? = null
|
|
32
32
|
private var wasMounted = false
|
|
33
|
+
private var callback: KeyboardAnimationCallback? = null
|
|
33
34
|
|
|
34
35
|
// region View lifecycles
|
|
35
36
|
override fun onAttachedToWindow() {
|
|
@@ -47,11 +48,7 @@ class EdgeToEdgeReactViewGroup(private val reactContext: ThemedReactContext) : R
|
|
|
47
48
|
override fun onDetachedFromWindow() {
|
|
48
49
|
super.onDetachedFromWindow()
|
|
49
50
|
|
|
50
|
-
|
|
51
|
-
// otherwise we may face NPE when app is getting opened via universal link
|
|
52
|
-
// see https://github.com/kirillzyusko/react-native-keyboard-controller/issues/242
|
|
53
|
-
// for more details
|
|
54
|
-
Handler(Looper.getMainLooper()).post { this.removeKeyboardCallbacks() }
|
|
51
|
+
this.removeKeyboardCallbacks()
|
|
55
52
|
}
|
|
56
53
|
// endregion
|
|
57
54
|
|
|
@@ -118,7 +115,7 @@ class EdgeToEdgeReactViewGroup(private val reactContext: ThemedReactContext) : R
|
|
|
118
115
|
val root = this.getContentView()
|
|
119
116
|
root?.addView(eventView)
|
|
120
117
|
|
|
121
|
-
|
|
118
|
+
callback = KeyboardAnimationCallback(
|
|
122
119
|
view = this,
|
|
123
120
|
persistentInsetTypes = WindowInsetsCompat.Type.systemBars(),
|
|
124
121
|
deferredInsetTypes = WindowInsetsCompat.Type.ime(),
|
|
@@ -137,7 +134,18 @@ class EdgeToEdgeReactViewGroup(private val reactContext: ThemedReactContext) : R
|
|
|
137
134
|
}
|
|
138
135
|
|
|
139
136
|
private fun removeKeyboardCallbacks() {
|
|
140
|
-
|
|
137
|
+
callback?.destroy()
|
|
138
|
+
|
|
139
|
+
// capture view into closure, because if `onDetachedFromWindow` and `onAttachedToWindow`
|
|
140
|
+
// dispatched synchronously after each other (open application on Fabric), then `.post`
|
|
141
|
+
// will destroy just newly created view (if we have a reference via `this`)
|
|
142
|
+
// and we'll have a memory leak or zombie-view
|
|
143
|
+
val view = eventView
|
|
144
|
+
// we need to remove view asynchronously from `onDetachedFromWindow` method
|
|
145
|
+
// otherwise we may face NPE when app is getting opened via universal link
|
|
146
|
+
// see https://github.com/kirillzyusko/react-native-keyboard-controller/issues/242
|
|
147
|
+
// for more details
|
|
148
|
+
Handler(Looper.getMainLooper()).post { view.removeSelf() }
|
|
141
149
|
}
|
|
142
150
|
|
|
143
151
|
private fun getContentView(): FitWindowsLinearLayout? {
|
package/ios/Extensions.swift
CHANGED
|
@@ -52,3 +52,10 @@ public extension Optional where Wrapped == UIResponder {
|
|
|
52
52
|
#endif
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
|
+
|
|
56
|
+
public extension UIView {
|
|
57
|
+
var globalFrame: CGRect? {
|
|
58
|
+
let rootView = UIApplication.shared.keyWindow?.rootViewController?.view
|
|
59
|
+
return superview?.convert(frame, to: rootView)
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
|
|
9
9
|
/* Begin PBXBuildFile section */
|
|
10
10
|
0807071E2A34807B00C05A19 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0807071D2A34807B00C05A19 /* Extensions.swift */; };
|
|
11
|
+
084AEEC62ACF49A8001A3069 /* FocusedInputLayoutChangedEvent.m in Sources */ = {isa = PBXBuildFile; fileRef = 084AEEC52ACF49A8001A3069 /* FocusedInputLayoutChangedEvent.m */; };
|
|
12
|
+
084AEEC82ACF4AB2001A3069 /* FocusedInputLayoutObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 084AEEC72ACF4AB2001A3069 /* FocusedInputLayoutObserver.swift */; };
|
|
11
13
|
F333F8D428996B8D0015B05F /* KeyboardControllerView.mm in Sources */ = {isa = PBXBuildFile; fileRef = F333F8D228996B8D0015B05F /* KeyboardControllerView.mm */; };
|
|
12
14
|
F359D34F28133C26000B6AFE /* KeyboardControllerModule.mm in Sources */ = {isa = PBXBuildFile; fileRef = F359D34E28133C26000B6AFE /* KeyboardControllerModule.mm */; };
|
|
13
15
|
F3626A0728B3FE760021B2D9 /* KeyboardMovementObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3626A0628B3FE760021B2D9 /* KeyboardMovementObserver.swift */; };
|
|
@@ -29,6 +31,9 @@
|
|
|
29
31
|
|
|
30
32
|
/* Begin PBXFileReference section */
|
|
31
33
|
0807071D2A34807B00C05A19 /* Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Extensions.swift; sourceTree = "<group>"; };
|
|
34
|
+
084AEEC22ACF479A001A3069 /* FocusedInputLayoutChangedEvent.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FocusedInputLayoutChangedEvent.h; sourceTree = "<group>"; };
|
|
35
|
+
084AEEC52ACF49A8001A3069 /* FocusedInputLayoutChangedEvent.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FocusedInputLayoutChangedEvent.m; sourceTree = "<group>"; };
|
|
36
|
+
084AEEC72ACF4AB2001A3069 /* FocusedInputLayoutObserver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FocusedInputLayoutObserver.swift; sourceTree = "<group>"; };
|
|
32
37
|
134814201AA4EA6300B7C361 /* libKeyboardController.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libKeyboardController.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
|
33
38
|
B3E7B5891CC2AC0600A0062D /* KeyboardControllerViewManager.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = KeyboardControllerViewManager.mm; sourceTree = "<group>"; };
|
|
34
39
|
F333F8D128996B1C0015B05F /* KeyboardControllerView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KeyboardControllerView.h; sourceTree = "<group>"; };
|
|
@@ -53,6 +58,37 @@
|
|
|
53
58
|
/* End PBXFrameworksBuildPhase section */
|
|
54
59
|
|
|
55
60
|
/* Begin PBXGroup section */
|
|
61
|
+
084AEEBF2ACDFBF1001A3069 /* events */ = {
|
|
62
|
+
isa = PBXGroup;
|
|
63
|
+
children = (
|
|
64
|
+
F3F50667289E653B003091D6 /* KeyboardMoveEvent.h */,
|
|
65
|
+
F3F50668289E653B003091D6 /* KeyboardMoveEvent.m */,
|
|
66
|
+
084AEEC22ACF479A001A3069 /* FocusedInputLayoutChangedEvent.h */,
|
|
67
|
+
084AEEC52ACF49A8001A3069 /* FocusedInputLayoutChangedEvent.m */,
|
|
68
|
+
);
|
|
69
|
+
path = events;
|
|
70
|
+
sourceTree = "<group>";
|
|
71
|
+
};
|
|
72
|
+
084AEEC02ACDFC2A001A3069 /* observers */ = {
|
|
73
|
+
isa = PBXGroup;
|
|
74
|
+
children = (
|
|
75
|
+
F3626A0628B3FE760021B2D9 /* KeyboardMovementObserver.swift */,
|
|
76
|
+
084AEEC72ACF4AB2001A3069 /* FocusedInputLayoutObserver.swift */,
|
|
77
|
+
);
|
|
78
|
+
path = observers;
|
|
79
|
+
sourceTree = "<group>";
|
|
80
|
+
};
|
|
81
|
+
084AEEC12ACF405C001A3069 /* views */ = {
|
|
82
|
+
isa = PBXGroup;
|
|
83
|
+
children = (
|
|
84
|
+
F333F8D228996B8D0015B05F /* KeyboardControllerView.mm */,
|
|
85
|
+
F333F8D128996B1C0015B05F /* KeyboardControllerView.h */,
|
|
86
|
+
F4FF95D6245B92E800C19C63 /* KeyboardControllerViewManager.swift */,
|
|
87
|
+
B3E7B5891CC2AC0600A0062D /* KeyboardControllerViewManager.mm */,
|
|
88
|
+
);
|
|
89
|
+
path = views;
|
|
90
|
+
sourceTree = "<group>";
|
|
91
|
+
};
|
|
56
92
|
134814211AA4EA7D00B7C361 /* Products */ = {
|
|
57
93
|
isa = PBXGroup;
|
|
58
94
|
children = (
|
|
@@ -64,16 +100,12 @@
|
|
|
64
100
|
58B511D21A9E6C8500147676 = {
|
|
65
101
|
isa = PBXGroup;
|
|
66
102
|
children = (
|
|
103
|
+
084AEEC12ACF405C001A3069 /* views */,
|
|
104
|
+
084AEEC02ACDFC2A001A3069 /* observers */,
|
|
105
|
+
084AEEBF2ACDFBF1001A3069 /* events */,
|
|
67
106
|
0807071D2A34807B00C05A19 /* Extensions.swift */,
|
|
68
|
-
F3F50667289E653B003091D6 /* KeyboardMoveEvent.h */,
|
|
69
|
-
F3F50668289E653B003091D6 /* KeyboardMoveEvent.m */,
|
|
70
|
-
F3626A0628B3FE760021B2D9 /* KeyboardMovementObserver.swift */,
|
|
71
|
-
F333F8D228996B8D0015B05F /* KeyboardControllerView.mm */,
|
|
72
|
-
F333F8D128996B1C0015B05F /* KeyboardControllerView.h */,
|
|
73
107
|
F359D35028133C6F000B6AFE /* KeyboardControllerModule-Header.h */,
|
|
74
108
|
F359D34E28133C26000B6AFE /* KeyboardControllerModule.mm */,
|
|
75
|
-
F4FF95D6245B92E800C19C63 /* KeyboardControllerViewManager.swift */,
|
|
76
|
-
B3E7B5891CC2AC0600A0062D /* KeyboardControllerViewManager.mm */,
|
|
77
109
|
F4FF95D5245B92E700C19C63 /* KeyboardController-Bridging-Header.h */,
|
|
78
110
|
134814211AA4EA7D00B7C361 /* Products */,
|
|
79
111
|
);
|
|
@@ -136,9 +168,11 @@
|
|
|
136
168
|
isa = PBXSourcesBuildPhase;
|
|
137
169
|
buildActionMask = 2147483647;
|
|
138
170
|
files = (
|
|
171
|
+
084AEEC62ACF49A8001A3069 /* FocusedInputLayoutChangedEvent.m in Sources */,
|
|
139
172
|
F3626A0728B3FE760021B2D9 /* KeyboardMovementObserver.swift in Sources */,
|
|
140
173
|
0807071E2A34807B00C05A19 /* Extensions.swift in Sources */,
|
|
141
174
|
F359D34F28133C26000B6AFE /* KeyboardControllerModule.mm in Sources */,
|
|
175
|
+
084AEEC82ACF4AB2001A3069 /* FocusedInputLayoutObserver.swift in Sources */,
|
|
142
176
|
F4FF95D7245B92E800C19C63 /* KeyboardControllerViewManager.swift in Sources */,
|
|
143
177
|
F333F8D428996B8D0015B05F /* KeyboardControllerView.mm in Sources */,
|
|
144
178
|
F3F50669289E653B003091D6 /* KeyboardMoveEvent.m in Sources */,
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
//
|
|
2
|
+
// FocusedInputLayoutChangedEvent.h
|
|
3
|
+
// KeyboardController
|
|
4
|
+
//
|
|
5
|
+
// Created by Kiryl Ziusko on 05/10/2023.
|
|
6
|
+
// Copyright © 2023 Facebook. All rights reserved.
|
|
7
|
+
//
|
|
8
|
+
|
|
9
|
+
#import <Foundation/Foundation.h>
|
|
10
|
+
#import <React/RCTEventDispatcherProtocol.h>
|
|
11
|
+
|
|
12
|
+
@interface FocusedInputLayoutChangedEvent : NSObject <RCTEvent>
|
|
13
|
+
|
|
14
|
+
- (instancetype)initWithReactTag:(NSNumber *)reactTag event:(NSObject *)event;
|
|
15
|
+
|
|
16
|
+
@end
|