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.
Files changed (46) hide show
  1. package/README.md +5 -56
  2. package/android/build.gradle +1 -1
  3. package/android/gradle.properties +2 -0
  4. package/android/src/main/java/com/reactnativekeyboardcontroller/EdgeToEdgeReactViewGroup.kt +2 -0
  5. package/android/src/main/java/com/reactnativekeyboardcontroller/KeyboardControllerPackage.kt +6 -6
  6. package/android/src/main/java/com/reactnativekeyboardcontroller/KeyboardControllerViewManager.kt +6 -2
  7. package/android/src/main/java/com/reactnativekeyboardcontroller/StatusBarManagerCompatModule.kt +81 -0
  8. package/android/src/main/java/com/reactnativekeyboardcontroller/TranslateDeferringInsetsAnimationCallback.kt +41 -41
  9. package/ios/.swift-version +1 -0
  10. package/ios/.swiftformat +5 -0
  11. package/ios/.swiftlint.yml +5 -0
  12. package/ios/KeyboardControllerModule.swift +17 -17
  13. package/ios/KeyboardControllerViewManager.swift +86 -54
  14. package/ios/KeyboardMoveEvent.swift +31 -27
  15. package/lib/commonjs/animated.js +14 -11
  16. package/lib/commonjs/animated.js.map +1 -1
  17. package/lib/commonjs/index.js +2 -0
  18. package/lib/commonjs/index.js.map +1 -1
  19. package/lib/commonjs/monkey-patch.js +44 -0
  20. package/lib/commonjs/monkey-patch.js.map +1 -0
  21. package/lib/module/animated.js +13 -12
  22. package/lib/module/animated.js.map +1 -1
  23. package/lib/module/index.js +1 -0
  24. package/lib/module/index.js.map +1 -1
  25. package/lib/module/monkey-patch.js +36 -0
  26. package/lib/module/monkey-patch.js.map +1 -0
  27. package/lib/typescript/animated.d.ts +5 -0
  28. package/lib/typescript/index.d.ts +1 -0
  29. package/lib/typescript/monkey-patch.d.ts +1 -0
  30. package/package.json +9 -6
  31. package/src/animated.tsx +20 -16
  32. package/src/index.ts +2 -0
  33. package/src/monkey-patch.tsx +36 -0
  34. package/android/.gradle/6.8/executionHistory/executionHistory.lock +0 -0
  35. package/android/.gradle/6.8/fileChanges/last-build.bin +0 -0
  36. package/android/.gradle/6.8/fileHashes/fileHashes.lock +0 -0
  37. package/android/.gradle/6.8/gc.properties +0 -0
  38. package/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
  39. package/android/.gradle/buildOutputCleanup/cache.properties +0 -2
  40. package/android/.gradle/checksums/checksums.lock +0 -0
  41. package/android/.gradle/configuration-cache/gc.properties +0 -0
  42. package/android/.gradle/vcs-1/gc.properties +0 -0
  43. package/ios/KeyboardController.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -7
  44. package/ios/KeyboardController.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +0 -8
  45. package/ios/KeyboardController.xcodeproj/project.xcworkspace/xcuserdata/kiryl.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  46. 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
- <div>
10
- <img align="right" width="28.65%" style="margin-left: 20px" src="./gifs/ios.gif?raw=true">
11
- <img align="right" width="30%" style="margin-left: 20px" src="./gifs/android.gif?raw=true">
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
- ## Usage
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
- ```js
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
 
@@ -41,7 +41,7 @@ android {
41
41
  }
42
42
  }
43
43
  lintOptions {
44
- disable 'GradleCompatible'
44
+ warningsAsErrors true
45
45
  }
46
46
  compileOptions {
47
47
  sourceCompatibility JavaVersion.VERSION_1_8
@@ -1,3 +1,5 @@
1
1
  KeyboardController_kotlinVersion=1.3.50
2
2
  KeyboardController_compileSdkVersion=29
3
3
  KeyboardController_targetSdkVersion=29
4
+
5
+ android.useAndroidX=true
@@ -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()
@@ -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
- override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
10
- return listOf(KeyboardControllerModule(reactContext))
11
- }
9
+ override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
10
+ return listOf(KeyboardControllerModule(reactContext), StatusBarManagerCompatModule(reactContext))
11
+ }
12
12
 
13
- override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
14
- return listOf(KeyboardControllerViewManager(reactContext))
15
- }
13
+ override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
14
+ return listOf(KeyboardControllerViewManager(reactContext))
15
+ }
16
16
  }
@@ -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
- reactContext.currentActivity!!.window!!.decorView,
40
+ decorView,
37
41
  TranslateDeferringInsetsAnimationCallback(
38
42
  view = view,
39
43
  persistentInsetTypes = WindowInsetsCompat.Type.systemBars(),
@@ -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
- require(persistentInsetTypes and deferredInsetTypes == 0) {
65
- "persistentInsetTypes and deferredInsetTypes can not contain any of " +
66
- " same WindowInsetsCompat.Type values"
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
- 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
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
@@ -0,0 +1,5 @@
1
+ --indent 2
2
+
3
+ --disable redundantReturn
4
+
5
+ --exclude Pods,Generated
@@ -0,0 +1,5 @@
1
+ disabled_rules:
2
+ - trailing_comma
3
+
4
+ excluded:
5
+ - Pods
@@ -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(_ mode: NSNumber!) {}
28
+ @objc func setInputMode(_: NSNumber!) {}
29
29
  @objc func setDefaultMode() {}
30
-
31
- @objc open override func supportedEvents() -> [String] {
30
+
31
+ @objc override open func supportedEvents() -> [String] {
32
32
  return [
33
- "KeyboardController::keyboardWillShow",
34
- "KeyboardController::keyboardDidShow",
35
- "KeyboardController::keyboardWillHide",
36
- "KeyboardController::keyboardDidHide"
33
+ "KeyboardController::keyboardWillShow",
34
+ "KeyboardController::keyboardDidShow",
35
+ "KeyboardController::keyboardWillHide",
36
+ "KeyboardController::keyboardDidHide",
37
37
  ]
38
38
  }
39
-
40
- @objc open override func startObserving() {
39
+
40
+ @objc override open func startObserving() {
41
41
  hasListeners = true
42
42
  }
43
-
44
- @objc open override func stopObserving() {
43
+
44
+ @objc override open func stopObserving() {
45
45
  hasListeners = false
46
46
  }
47
-
48
- @objc open override func sendEvent(withName name: String!, body: Any!) {
49
- if (hasListeners) {
50
- super.sendEvent(withName: name, body: body)
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
- return KeyboardControllerView(frame: CGRect.zero, eventDispatcher: self.bridge.eventDispatcher())
8
+ return KeyboardControllerView(frame: CGRect.zero, eventDispatcher: bridge.eventDispatcher())
9
9
  }
10
10
  }
11
11
 
12
- class KeyboardControllerView : UIView {
13
- private var eventDispatcher: RCTEventDispatcherProtocol
14
- @objc var onKeyboardMove: RCTDirectEventBlock?
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
- @objc func keyboardWillAppear(_ notification: Notification) {
34
- if let keyboardFrame: NSValue = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue {
35
- let keyboardHeight = keyboardFrame.cgRectValue.size.height
16
+ init(frame: CGRect, eventDispatcher: RCTEventDispatcherProtocol) {
17
+ self.eventDispatcher = eventDispatcher
18
+ super.init(frame: frame)
19
+ }
36
20
 
37
- self.eventDispatcher.send(KeyboardMoveEvent(reactTag: self.reactTag, height: -keyboardHeight as NSNumber, progress: 1))
21
+ @available(*, unavailable)
22
+ required init?(coder _: NSCoder) {
23
+ fatalError("init(coder:) has not been implemented")
24
+ }
38
25
 
39
- var data = [AnyHashable: Any]()
40
- data["height"] = keyboardHeight
41
- KeyboardController.shared?.sendEvent(withName: "KeyboardController::keyboardWillShow", body: data)
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
- @objc func keyboardWillDisappear() {
46
- self.eventDispatcher.send(KeyboardMoveEvent(reactTag: self.reactTag, height: 0, progress: 0))
47
-
48
- var data = [AnyHashable: Any]()
49
- data["height"] = 0
50
- KeyboardController.shared?.sendEvent(withName: "KeyboardController::keyboardWillHide", body: data)
51
- }
52
-
53
- @objc func keyboardDidAppear(_ notification: Notification) {
54
- if let keyboardFrame: NSValue = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue {
55
- let keyboardHeight = keyboardFrame.cgRectValue.size.height
56
-
57
- var data = [AnyHashable: Any]()
58
- data["height"] = keyboardHeight
59
-
60
- KeyboardController.shared?.sendEvent(withName: "KeyboardController::keyboardDidShow", body: data)
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
- @objc func keyboardDidDisappear() {
65
- var data = [AnyHashable: Any]()
66
- data["height"] = 0
67
- KeyboardController.shared?.sendEvent(withName: "KeyboardController::keyboardDidHide", body: data)
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
- override func willRemoveSubview(_ subview: UIView) {
71
- super.willRemoveSubview(subview)
72
- NotificationCenter.default.removeObserver(self) // TODO: correct place?
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
- 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 [self.viewTag, RCTNormalizeInputEventName(self.eventName), ["height": self.height, "progress": self.progress]]
30
- }
31
-
32
- init(reactTag: NSNumber, height: NSNumber, progress: NSNumber) {
33
- self.viewTag = reactTag
34
- self.height = height
35
- self.progress = progress
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
  }