react-native-keyboard-controller 1.0.0-alpha.2 → 1.0.0
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 +5 -56
- package/android/build.gradle +1 -1
- package/android/gradle.properties +2 -0
- package/android/src/main/java/com/reactnativekeyboardcontroller/EdgeToEdgeReactViewGroup.kt +2 -0
- package/android/src/main/java/com/reactnativekeyboardcontroller/KeyboardControllerPackage.kt +6 -6
- package/android/src/main/java/com/reactnativekeyboardcontroller/KeyboardControllerViewManager.kt +6 -2
- package/android/src/main/java/com/reactnativekeyboardcontroller/StatusBarManagerCompatModule.kt +81 -0
- package/android/src/main/java/com/reactnativekeyboardcontroller/TranslateDeferringInsetsAnimationCallback.kt +41 -41
- package/ios/.swift-version +1 -0
- package/ios/.swiftformat +5 -0
- package/ios/.swiftlint.yml +5 -0
- package/ios/KeyboardControllerModule.swift +17 -17
- package/ios/KeyboardControllerViewManager.swift +86 -54
- package/ios/KeyboardMoveEvent.swift +31 -27
- package/lib/commonjs/animated.js +14 -11
- package/lib/commonjs/animated.js.map +1 -1
- package/lib/commonjs/index.js +2 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/monkey-patch.js +44 -0
- package/lib/commonjs/monkey-patch.js.map +1 -0
- package/lib/module/animated.js +13 -12
- package/lib/module/animated.js.map +1 -1
- package/lib/module/index.js +1 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/monkey-patch.js +36 -0
- package/lib/module/monkey-patch.js.map +1 -0
- package/lib/typescript/animated.d.ts +5 -0
- package/lib/typescript/index.d.ts +1 -0
- package/lib/typescript/monkey-patch.d.ts +1 -0
- package/package.json +9 -6
- package/src/animated.tsx +20 -16
- package/src/index.ts +2 -0
- package/src/monkey-patch.tsx +36 -0
- package/android/.gradle/6.8/executionHistory/executionHistory.lock +0 -0
- package/android/.gradle/6.8/fileChanges/last-build.bin +0 -0
- package/android/.gradle/6.8/fileHashes/fileHashes.lock +0 -0
- package/android/.gradle/6.8/gc.properties +0 -0
- package/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
- package/android/.gradle/buildOutputCleanup/cache.properties +0 -2
- package/android/.gradle/checksums/checksums.lock +0 -0
- package/android/.gradle/configuration-cache/gc.properties +0 -0
- package/android/.gradle/vcs-1/gc.properties +0 -0
- package/ios/KeyboardController.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -7
- package/ios/KeyboardController.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +0 -8
- package/ios/KeyboardController.xcodeproj/project.xcworkspace/xcuserdata/kiryl.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/ios/KeyboardController.xcodeproj/xcuserdata/kiryl.xcuserdatad/xcschemes/xcschememanagement.plist +0 -14
package/README.md
CHANGED
|
@@ -2,14 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
Keyboard manager which works in identical way on both iOS and Android.
|
|
4
4
|
|
|
5
|
-
> **Note**: This library is still in development and in `alpha` stage. So most likely it has bugs/issues - don't hesitate to report if you find them 🙂.
|
|
6
|
-
|
|
7
5
|
## Demonstration
|
|
8
6
|
|
|
9
|
-
<
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
</div>
|
|
7
|
+
<img src="./gifs/demo.gif?raw=true" width="60%">
|
|
8
|
+
|
|
9
|
+
## Key features
|
|
13
10
|
|
|
14
11
|
- mapping keyboard appearance to animated values 😎
|
|
15
12
|
- missing `keyboardWillShow` / `keyboardWillHide` events are available on Android 😍
|
|
@@ -18,8 +15,6 @@ Keyboard manager which works in identical way on both iOS and Android.
|
|
|
18
15
|
- interactive keyboard dismissing (planned) 👆📱
|
|
19
16
|
- and more is coming... Stay tuned! 😊
|
|
20
17
|
|
|
21
|
-
<br clear="right" />
|
|
22
|
-
|
|
23
18
|
## Installation
|
|
24
19
|
|
|
25
20
|
Install `react-native-keyboard-controller` package from npm:
|
|
@@ -30,55 +25,9 @@ yarn add react-native-keyboard-controller
|
|
|
30
25
|
# npm install react-native-keyboard-controller --save
|
|
31
26
|
```
|
|
32
27
|
|
|
33
|
-
##
|
|
34
|
-
|
|
35
|
-
For more comprehensive usage you can have a look on [example](https://github.com/kirillzyusko/react-native-keyboard-controller/tree/main/example).
|
|
36
|
-
|
|
37
|
-
Below you can see a short overview of library API:
|
|
28
|
+
## Documentation
|
|
38
29
|
|
|
39
|
-
|
|
40
|
-
import {
|
|
41
|
-
KeyboardProvider,
|
|
42
|
-
useKeyboardAnimation,
|
|
43
|
-
} from 'react-native-keyboard-controller';
|
|
44
|
-
|
|
45
|
-
// 1. wrap your app in Provider
|
|
46
|
-
<KeyboardProvider>
|
|
47
|
-
<AppContainer />
|
|
48
|
-
</KeyboardProvider>
|
|
49
|
-
|
|
50
|
-
// 2. get animation values where you need them
|
|
51
|
-
const { height, progress } = useKeyboardAnimation();
|
|
52
|
-
|
|
53
|
-
// 3. Animate any elements as you wish :)
|
|
54
|
-
<Animated.View
|
|
55
|
-
style={{
|
|
56
|
-
width: 50,
|
|
57
|
-
height: 50,
|
|
58
|
-
backgroundColor: 'red',
|
|
59
|
-
borderRadius: 25,
|
|
60
|
-
// the element will move up with the keyboard
|
|
61
|
-
transform: [{ translateY: height }],
|
|
62
|
-
}}
|
|
63
|
-
/>
|
|
64
|
-
<Animated.View
|
|
65
|
-
style={{
|
|
66
|
-
width: 50,
|
|
67
|
-
height: 50,
|
|
68
|
-
backgroundColor: 'green',
|
|
69
|
-
borderRadius: 25,
|
|
70
|
-
transform: [
|
|
71
|
-
{
|
|
72
|
-
// or use custom interpolation using `progress`
|
|
73
|
-
translateX: progress.interpolate({
|
|
74
|
-
inputRange: [0, 1],
|
|
75
|
-
outputRange: [0, 100],
|
|
76
|
-
}),
|
|
77
|
-
},
|
|
78
|
-
],
|
|
79
|
-
}}
|
|
80
|
-
/>
|
|
81
|
-
```
|
|
30
|
+
Check out our dedicated documentation page for info about this library, API reference and more: [https://kirillzyusko.github.io/react-native-keyboard-controller/](https://kirillzyusko.github.io/react-native-keyboard-controller/)
|
|
82
31
|
|
|
83
32
|
## Contributing
|
|
84
33
|
|
package/android/build.gradle
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
package com.reactnativekeyboardcontroller
|
|
2
2
|
|
|
3
|
+
import android.annotation.SuppressLint
|
|
3
4
|
import androidx.core.view.WindowCompat
|
|
4
5
|
import com.facebook.react.uimanager.ThemedReactContext
|
|
5
6
|
import com.facebook.react.views.view.ReactViewGroup
|
|
6
7
|
|
|
8
|
+
@SuppressLint("ViewConstructor")
|
|
7
9
|
class EdgeToEdgeReactViewGroup(private val reactContext: ThemedReactContext) : ReactViewGroup(reactContext) {
|
|
8
10
|
override fun onAttachedToWindow() {
|
|
9
11
|
super.onAttachedToWindow()
|
package/android/src/main/java/com/reactnativekeyboardcontroller/KeyboardControllerPackage.kt
CHANGED
|
@@ -6,11 +6,11 @@ import com.facebook.react.bridge.ReactApplicationContext
|
|
|
6
6
|
import com.facebook.react.uimanager.ViewManager
|
|
7
7
|
|
|
8
8
|
class KeyboardControllerPackage : ReactPackage {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
|
|
10
|
+
return listOf(KeyboardControllerModule(reactContext), StatusBarManagerCompatModule(reactContext))
|
|
11
|
+
}
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
|
|
14
|
+
return listOf(KeyboardControllerViewManager(reactContext))
|
|
15
|
+
}
|
|
16
16
|
}
|
package/android/src/main/java/com/reactnativekeyboardcontroller/KeyboardControllerViewManager.kt
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
package com.reactnativekeyboardcontroller
|
|
2
2
|
|
|
3
3
|
import androidx.appcompat.widget.FitWindowsLinearLayout
|
|
4
|
-
import androidx.core.view
|
|
4
|
+
import androidx.core.view.ViewCompat
|
|
5
|
+
import androidx.core.view.WindowInsetsAnimationCompat
|
|
6
|
+
import androidx.core.view.WindowInsetsCompat
|
|
5
7
|
import com.facebook.react.bridge.ReactApplicationContext
|
|
6
8
|
import com.facebook.react.common.MapBuilder
|
|
7
9
|
import com.facebook.react.uimanager.ThemedReactContext
|
|
@@ -18,6 +20,8 @@ class KeyboardControllerViewManager(reactContext: ReactApplicationContext) : Rea
|
|
|
18
20
|
|
|
19
21
|
override fun createViewInstance(reactContext: ThemedReactContext): ReactViewGroup {
|
|
20
22
|
val view = EdgeToEdgeReactViewGroup(reactContext)
|
|
23
|
+
val window = mReactContext.currentActivity!!.window
|
|
24
|
+
val decorView = window.decorView
|
|
21
25
|
|
|
22
26
|
ViewCompat.setOnApplyWindowInsetsListener(view) { v, insets ->
|
|
23
27
|
val content =
|
|
@@ -33,7 +37,7 @@ class KeyboardControllerViewManager(reactContext: ReactApplicationContext) : Rea
|
|
|
33
37
|
}
|
|
34
38
|
|
|
35
39
|
ViewCompat.setWindowInsetsAnimationCallback(
|
|
36
|
-
|
|
40
|
+
decorView,
|
|
37
41
|
TranslateDeferringInsetsAnimationCallback(
|
|
38
42
|
view = view,
|
|
39
43
|
persistentInsetTypes = WindowInsetsCompat.Type.systemBars(),
|
package/android/src/main/java/com/reactnativekeyboardcontroller/StatusBarManagerCompatModule.kt
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
package com.reactnativekeyboardcontroller
|
|
2
|
+
|
|
3
|
+
import android.animation.ArgbEvaluator
|
|
4
|
+
import android.animation.ValueAnimator
|
|
5
|
+
import android.os.Build
|
|
6
|
+
import androidx.annotation.RequiresApi
|
|
7
|
+
import androidx.core.view.WindowInsetsCompat
|
|
8
|
+
import androidx.core.view.WindowInsetsControllerCompat
|
|
9
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
10
|
+
import com.facebook.react.bridge.ReactContextBaseJavaModule
|
|
11
|
+
import com.facebook.react.bridge.ReactMethod
|
|
12
|
+
import com.facebook.react.bridge.UiThreadUtil
|
|
13
|
+
|
|
14
|
+
class StatusBarManagerCompatModule(private val mReactContext: ReactApplicationContext) : ReactContextBaseJavaModule(mReactContext) {
|
|
15
|
+
private var controller: WindowInsetsControllerCompat? = null
|
|
16
|
+
|
|
17
|
+
override fun getName(): String = "StatusBarManagerCompat"
|
|
18
|
+
|
|
19
|
+
@ReactMethod
|
|
20
|
+
private fun setHidden(hidden: Boolean) {
|
|
21
|
+
UiThreadUtil.runOnUiThread {
|
|
22
|
+
if (hidden) {
|
|
23
|
+
getController()?.hide(WindowInsetsCompat.Type.statusBars())
|
|
24
|
+
} else {
|
|
25
|
+
getController()?.show(WindowInsetsCompat.Type.statusBars())
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
|
|
31
|
+
@ReactMethod
|
|
32
|
+
private fun setColor(color: Int, animated: Boolean) {
|
|
33
|
+
UiThreadUtil.runOnUiThread {
|
|
34
|
+
val window = mReactContext.currentActivity!!.window
|
|
35
|
+
|
|
36
|
+
if (animated) {
|
|
37
|
+
val curColor: Int = window.statusBarColor
|
|
38
|
+
val colorAnimation = ValueAnimator.ofObject(ArgbEvaluator(), curColor, color)
|
|
39
|
+
colorAnimation.addUpdateListener { animator ->
|
|
40
|
+
window.statusBarColor = animator.animatedValue as Int
|
|
41
|
+
}
|
|
42
|
+
colorAnimation.setDuration(300).startDelay = 0
|
|
43
|
+
colorAnimation.start()
|
|
44
|
+
} else {
|
|
45
|
+
window.statusBarColor = color
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
@ReactMethod
|
|
51
|
+
private fun setTranslucent(translucent: Boolean) {
|
|
52
|
+
// the status bar is translucent by default (once you wrapped App in Provider,
|
|
53
|
+
// and EdgeToEdgeReactViewGroup has been mounted and called
|
|
54
|
+
// `setDecorFitsSystemWindows(window, false)`. By default this library applies default padding
|
|
55
|
+
// which equal to StatusBar height, so it will have a default RN app behavior. Though once you
|
|
56
|
+
// need to set StatusBar as translucent, you will need to use `statusBarTranslucent` prop on
|
|
57
|
+
// `KeyboardProvider` (it will preventing of applying additional padding, and status bar will be
|
|
58
|
+
// translucent. Though it's important to note, that this value is not reactive (i. e. if you change
|
|
59
|
+
// `statusBarTranslucent` in runtime it will not have any effect. Just theoretically I could make
|
|
60
|
+
// it reactive, but I know, that most of apps or don't use StatusBar translucency at all or they are
|
|
61
|
+
// specifying it for entire app, so I don't see a lot of sense to make it reactive as of now. If your
|
|
62
|
+
// app requires to dynamically manage it - just shoot an issue and I will try to add a support fot that.
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
@ReactMethod
|
|
66
|
+
private fun setStyle(style: String) {
|
|
67
|
+
UiThreadUtil.runOnUiThread {
|
|
68
|
+
getController()?.isAppearanceLightStatusBars = style == "dark-content"
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
private fun getController(): WindowInsetsControllerCompat? {
|
|
73
|
+
if (this.controller == null) {
|
|
74
|
+
val window = mReactContext.currentActivity!!.window
|
|
75
|
+
|
|
76
|
+
this.controller = WindowInsetsControllerCompat(window, window.decorView)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return this.controller
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -61,10 +61,10 @@ class TranslateDeferringInsetsAnimationCallback(
|
|
|
61
61
|
private var persistentKeyboardHeight = 0
|
|
62
62
|
|
|
63
63
|
init {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
64
|
+
require(persistentInsetTypes and deferredInsetTypes == 0) {
|
|
65
|
+
"persistentInsetTypes and deferredInsetTypes can not contain any of " +
|
|
66
|
+
" same WindowInsetsCompat.Type values"
|
|
67
|
+
}
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
override fun onStart(
|
|
@@ -81,9 +81,9 @@ class TranslateDeferringInsetsAnimationCallback(
|
|
|
81
81
|
|
|
82
82
|
val params: WritableMap = Arguments.createMap()
|
|
83
83
|
params.putInt("height", keyboardHeight)
|
|
84
|
-
context?.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)?.emit("KeyboardController::" + if(!isKeyboardVisible) "keyboardWillHide" else "keyboardWillShow", params)
|
|
84
|
+
context?.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)?.emit("KeyboardController::" + if (!isKeyboardVisible) "keyboardWillHide" else "keyboardWillShow", params)
|
|
85
85
|
|
|
86
|
-
Log.i(TAG, "KeyboardController::" + if(!isKeyboardVisible) "keyboardWillHide" else "keyboardWillShow")
|
|
86
|
+
Log.i(TAG, "KeyboardController::" + if (!isKeyboardVisible) "keyboardWillHide" else "keyboardWillShow")
|
|
87
87
|
Log.i(TAG, "HEIGHT:: $keyboardHeight")
|
|
88
88
|
|
|
89
89
|
return super.onStart(animation, bounds)
|
|
@@ -95,9 +95,9 @@ class TranslateDeferringInsetsAnimationCallback(
|
|
|
95
95
|
val isKeyboardVisible = isKeyboardVisible()
|
|
96
96
|
|
|
97
97
|
val params: WritableMap = Arguments.createMap()
|
|
98
|
-
context?.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)?.emit("KeyboardController::" + if(!isKeyboardVisible) "keyboardDidHide" else "keyboardDidShow", params)
|
|
98
|
+
context?.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)?.emit("KeyboardController::" + if (!isKeyboardVisible) "keyboardDidHide" else "keyboardDidShow", params)
|
|
99
99
|
|
|
100
|
-
Log.i(TAG, "KeyboardController::" + if(!isKeyboardVisible) "keyboardDidHide" else "keyboardDidShow")
|
|
100
|
+
Log.i(TAG, "KeyboardController::" + if (!isKeyboardVisible) "keyboardDidHide" else "keyboardDidShow")
|
|
101
101
|
}
|
|
102
102
|
|
|
103
103
|
private fun isKeyboardVisible(): Boolean {
|
|
@@ -115,38 +115,38 @@ class TranslateDeferringInsetsAnimationCallback(
|
|
|
115
115
|
return Math.max(toDp((keyboardHeight - navigationBar).toFloat(), context!!), 0)
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
}
|
|
143
|
-
Log.i(TAG, "DiffY: $diffY $height")
|
|
144
|
-
|
|
145
|
-
context
|
|
146
|
-
.getNativeModule(UIManagerModule::class.java)
|
|
147
|
-
?.eventDispatcher
|
|
148
|
-
?.dispatchEvent(KeyboardTransitionEvent(view.id, height, progress))
|
|
149
|
-
|
|
150
|
-
return insets
|
|
118
|
+
override fun onProgress(
|
|
119
|
+
insets: WindowInsetsCompat,
|
|
120
|
+
runningAnimations: List<WindowInsetsAnimationCompat>
|
|
121
|
+
): WindowInsetsCompat {
|
|
122
|
+
// onProgress() is called when any of the running animations progress...
|
|
123
|
+
|
|
124
|
+
// First we get the insets which are potentially deferred
|
|
125
|
+
val typesInset = insets.getInsets(deferredInsetTypes)
|
|
126
|
+
// Then we get the persistent inset types which are applied as padding during layout
|
|
127
|
+
val otherInset = insets.getInsets(persistentInsetTypes)
|
|
128
|
+
|
|
129
|
+
// Now that we subtract the two insets, to calculate the difference. We also coerce
|
|
130
|
+
// the insets to be >= 0, to make sure we don't use negative insets.
|
|
131
|
+
val diff = Insets.subtract(typesInset, otherInset).let {
|
|
132
|
+
Insets.max(it, Insets.NONE)
|
|
133
|
+
}
|
|
134
|
+
val diffY = (diff.top - diff.bottom).toFloat()
|
|
135
|
+
val height = toDp(diffY, context!!)
|
|
136
|
+
|
|
137
|
+
var progress = 0.0
|
|
138
|
+
try {
|
|
139
|
+
progress = Math.abs((height.toDouble() / persistentKeyboardHeight))
|
|
140
|
+
} catch (e: ArithmeticException) {
|
|
141
|
+
// do nothing, send progress as 0
|
|
151
142
|
}
|
|
143
|
+
Log.i(TAG, "DiffY: $diffY $height")
|
|
144
|
+
|
|
145
|
+
context
|
|
146
|
+
.getNativeModule(UIManagerModule::class.java)
|
|
147
|
+
?.eventDispatcher
|
|
148
|
+
?.dispatchEvent(KeyboardTransitionEvent(view.id, height, progress))
|
|
149
|
+
|
|
150
|
+
return insets
|
|
151
|
+
}
|
|
152
152
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
5.6
|
package/ios/.swiftformat
ADDED
|
@@ -6,15 +6,15 @@
|
|
|
6
6
|
// Copyright © 2022 Facebook. All rights reserved.
|
|
7
7
|
//
|
|
8
8
|
|
|
9
|
+
import AVFoundation
|
|
9
10
|
import Foundation
|
|
10
11
|
import UIKit
|
|
11
|
-
import AVFoundation
|
|
12
12
|
|
|
13
13
|
@objc(KeyboardController)
|
|
14
14
|
class KeyboardController: RCTEventEmitter {
|
|
15
15
|
public static var shared: KeyboardController?
|
|
16
16
|
private var hasListeners = false
|
|
17
|
-
|
|
17
|
+
|
|
18
18
|
override class func requiresMainQueueSetup() -> Bool {
|
|
19
19
|
return false
|
|
20
20
|
}
|
|
@@ -25,29 +25,29 @@ class KeyboardController: RCTEventEmitter {
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
// Android stubs
|
|
28
|
-
@objc func setInputMode(_
|
|
28
|
+
@objc func setInputMode(_: NSNumber!) {}
|
|
29
29
|
@objc func setDefaultMode() {}
|
|
30
|
-
|
|
31
|
-
@objc open
|
|
30
|
+
|
|
31
|
+
@objc override open func supportedEvents() -> [String] {
|
|
32
32
|
return [
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
33
|
+
"KeyboardController::keyboardWillShow",
|
|
34
|
+
"KeyboardController::keyboardDidShow",
|
|
35
|
+
"KeyboardController::keyboardWillHide",
|
|
36
|
+
"KeyboardController::keyboardDidHide",
|
|
37
37
|
]
|
|
38
38
|
}
|
|
39
|
-
|
|
40
|
-
@objc open
|
|
39
|
+
|
|
40
|
+
@objc override open func startObserving() {
|
|
41
41
|
hasListeners = true
|
|
42
42
|
}
|
|
43
|
-
|
|
44
|
-
@objc open
|
|
43
|
+
|
|
44
|
+
@objc override open func stopObserving() {
|
|
45
45
|
hasListeners = false
|
|
46
46
|
}
|
|
47
|
-
|
|
48
|
-
@objc open
|
|
49
|
-
if
|
|
50
|
-
|
|
47
|
+
|
|
48
|
+
@objc override open func sendEvent(withName name: String!, body: Any!) {
|
|
49
|
+
if hasListeners {
|
|
50
|
+
super.sendEvent(withName: name, body: body)
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
53
|
}
|
|
@@ -5,70 +5,102 @@ class KeyboardControllerViewManager: RCTViewManager {
|
|
|
5
5
|
}
|
|
6
6
|
|
|
7
7
|
override func view() -> (KeyboardControllerView) {
|
|
8
|
-
|
|
8
|
+
return KeyboardControllerView(frame: CGRect.zero, eventDispatcher: bridge.eventDispatcher())
|
|
9
9
|
}
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
class KeyboardControllerView
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
init(frame: CGRect, eventDispatcher: RCTEventDispatcherProtocol) {
|
|
17
|
-
self.eventDispatcher = eventDispatcher
|
|
18
|
-
super.init(frame: frame)
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
required init?(coder: NSCoder) {
|
|
22
|
-
fatalError("init(coder:) has not been implemented")
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
override func willMove(toWindow newWindow: UIWindow?) {
|
|
26
|
-
super.willMove(toWindow: newWindow)
|
|
27
|
-
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillDisappear), name: UIResponder.keyboardWillHideNotification, object: nil)
|
|
28
|
-
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillAppear), name: UIResponder.keyboardWillShowNotification, object: nil)
|
|
29
|
-
NotificationCenter.default.addObserver(self, selector: #selector(keyboardDidAppear), name: UIResponder.keyboardDidShowNotification, object: nil)
|
|
30
|
-
NotificationCenter.default.addObserver(self, selector: #selector(keyboardDidDisappear), name: UIResponder.keyboardDidHideNotification, object: nil)
|
|
31
|
-
}
|
|
12
|
+
class KeyboardControllerView: UIView {
|
|
13
|
+
private var eventDispatcher: RCTEventDispatcherProtocol
|
|
14
|
+
@objc var onKeyboardMove: RCTDirectEventBlock?
|
|
32
15
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
16
|
+
init(frame: CGRect, eventDispatcher: RCTEventDispatcherProtocol) {
|
|
17
|
+
self.eventDispatcher = eventDispatcher
|
|
18
|
+
super.init(frame: frame)
|
|
19
|
+
}
|
|
36
20
|
|
|
37
|
-
|
|
21
|
+
@available(*, unavailable)
|
|
22
|
+
required init?(coder _: NSCoder) {
|
|
23
|
+
fatalError("init(coder:) has not been implemented")
|
|
24
|
+
}
|
|
38
25
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
26
|
+
override func willMove(toWindow newWindow: UIWindow?) {
|
|
27
|
+
if newWindow == nil {
|
|
28
|
+
// Will be removed from a window
|
|
29
|
+
// swiftlint:disable notification_center_detachment
|
|
30
|
+
NotificationCenter.default.removeObserver(self)
|
|
43
31
|
}
|
|
32
|
+
}
|
|
44
33
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
34
|
+
override func didMoveToWindow() {
|
|
35
|
+
if window != nil {
|
|
36
|
+
// Added to a window
|
|
37
|
+
NotificationCenter.default.addObserver(
|
|
38
|
+
self,
|
|
39
|
+
selector: #selector(keyboardWillDisappear),
|
|
40
|
+
name: UIResponder.keyboardWillHideNotification,
|
|
41
|
+
object: nil
|
|
42
|
+
)
|
|
43
|
+
NotificationCenter.default.addObserver(
|
|
44
|
+
self,
|
|
45
|
+
selector: #selector(keyboardWillAppear),
|
|
46
|
+
name: UIResponder.keyboardWillShowNotification,
|
|
47
|
+
object: nil
|
|
48
|
+
)
|
|
49
|
+
NotificationCenter.default.addObserver(
|
|
50
|
+
self,
|
|
51
|
+
selector: #selector(keyboardDidAppear),
|
|
52
|
+
name: UIResponder.keyboardDidShowNotification,
|
|
53
|
+
object: nil
|
|
54
|
+
)
|
|
55
|
+
NotificationCenter.default.addObserver(
|
|
56
|
+
self,
|
|
57
|
+
selector: #selector(keyboardDidDisappear),
|
|
58
|
+
name: UIResponder.keyboardDidHideNotification,
|
|
59
|
+
object: nil
|
|
60
|
+
)
|
|
62
61
|
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
@objc func keyboardWillAppear(_ notification: Notification) {
|
|
65
|
+
if let keyboardFrame: NSValue = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue {
|
|
66
|
+
let keyboardHeight = keyboardFrame.cgRectValue.size.height
|
|
67
|
+
|
|
68
|
+
eventDispatcher.send(
|
|
69
|
+
KeyboardMoveEvent(
|
|
70
|
+
viewTag: reactTag,
|
|
71
|
+
height: -keyboardHeight as NSNumber,
|
|
72
|
+
progress: 1
|
|
73
|
+
)
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
var data = [AnyHashable: Any]()
|
|
77
|
+
data["height"] = keyboardHeight
|
|
78
|
+
KeyboardController.shared?.sendEvent(withName: "KeyboardController::keyboardWillShow", body: data)
|
|
68
79
|
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
@objc func keyboardWillDisappear() {
|
|
83
|
+
eventDispatcher.send(KeyboardMoveEvent(viewTag: reactTag, height: 0, progress: 0))
|
|
84
|
+
|
|
85
|
+
var data = [AnyHashable: Any]()
|
|
86
|
+
data["height"] = 0
|
|
87
|
+
KeyboardController.shared?.sendEvent(withName: "KeyboardController::keyboardWillHide", body: data)
|
|
88
|
+
}
|
|
69
89
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
90
|
+
@objc func keyboardDidAppear(_ notification: Notification) {
|
|
91
|
+
if let keyboardFrame: NSValue = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue {
|
|
92
|
+
let keyboardHeight = keyboardFrame.cgRectValue.size.height
|
|
93
|
+
|
|
94
|
+
var data = [AnyHashable: Any]()
|
|
95
|
+
data["height"] = keyboardHeight
|
|
96
|
+
|
|
97
|
+
KeyboardController.shared?.sendEvent(withName: "KeyboardController::keyboardDidShow", body: data)
|
|
73
98
|
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
@objc func keyboardDidDisappear() {
|
|
102
|
+
var data = [AnyHashable: Any]()
|
|
103
|
+
data["height"] = 0
|
|
104
|
+
KeyboardController.shared?.sendEvent(withName: "KeyboardController::keyboardDidHide", body: data)
|
|
105
|
+
}
|
|
74
106
|
}
|
|
@@ -7,31 +7,35 @@
|
|
|
7
7
|
//
|
|
8
8
|
|
|
9
9
|
class KeyboardMoveEvent: NSObject, RCTEvent {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
10
|
+
var viewTag: NSNumber!
|
|
11
|
+
var eventName: String = "onKeyboardMove"
|
|
12
|
+
var coalescingKey: UInt16 = 0
|
|
13
|
+
var height: NSNumber!
|
|
14
|
+
var progress: NSNumber!
|
|
15
|
+
|
|
16
|
+
func canCoalesce() -> Bool {
|
|
17
|
+
return false
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
func coalesce(with newEvent: RCTEvent!) -> RCTEvent! {
|
|
21
|
+
return newEvent
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
static func moduleDotMethod() -> String! {
|
|
25
|
+
return "RCTEventEmitter.receiveEvent"
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
func arguments() -> [Any]! {
|
|
29
|
+
return [
|
|
30
|
+
viewTag,
|
|
31
|
+
RCTNormalizeInputEventName(eventName),
|
|
32
|
+
["height": height, "progress": progress],
|
|
33
|
+
]
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
init(viewTag: NSNumber, height: NSNumber, progress: NSNumber) {
|
|
37
|
+
self.viewTag = viewTag
|
|
38
|
+
self.height = height
|
|
39
|
+
self.progress = progress
|
|
40
|
+
}
|
|
37
41
|
}
|