├── .eslintignore ├── .eslintrc.js ├── .gitattributes ├── .gitignore ├── .prettierrc.js ├── LICENSE ├── README.md ├── RNKeyboardInsets.podspec ├── android ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── com │ └── reactnative │ └── keyboardinsets │ ├── EdgeInsets.java │ ├── KeyboardAutoHandler.java │ ├── KeyboardInsetsCallback.java │ ├── KeyboardInsetsModule.java │ ├── KeyboardInsetsPackage.java │ ├── KeyboardInsetsView.java │ ├── KeyboardInsetsViewManager.java │ ├── KeyboardManualHandler.java │ ├── KeyboardPositionChangedEvent.java │ ├── KeyboardStatusChangedEvent.java │ └── SystemUI.java ├── babel.config.js ├── example ├── App.tsx ├── KeyboardChat │ ├── Message │ │ ├── data.ts │ │ ├── index.tsx │ │ ├── styles.ts │ │ └── types.ts │ ├── driver │ │ ├── Driver.ts │ │ ├── KeyboardDriver.ts │ │ └── ViewDriver.ts │ ├── icon │ │ ├── emoji@2x.png │ │ ├── emoji@3x.png │ │ ├── keyboard@2x.png │ │ ├── keyboard@3x.png │ │ ├── plus@2x.png │ │ └── plus@3x.png │ ├── index.tsx │ └── styles.ts ├── android │ ├── app │ │ ├── build.gradle │ │ ├── debug.keystore │ │ ├── proguard-rules.pro │ │ └── src │ │ │ ├── debug │ │ │ └── AndroidManifest.xml │ │ │ └── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── java │ │ │ └── com │ │ │ │ └── reactnative │ │ │ │ └── keyboardinsets │ │ │ │ └── example │ │ │ │ ├── MainActivity.java │ │ │ │ └── MainApplication.java │ │ │ └── res │ │ │ ├── mipmap-hdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-mdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ └── values │ │ │ ├── strings.xml │ │ │ └── styles.xml │ ├── build.gradle │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ └── settings.gradle ├── index.js └── ios │ ├── KeyboardInsetsExample.xcodeproj │ ├── project.pbxproj │ └── xcshareddata │ │ └── xcschemes │ │ └── KeyboardInsetsExample.xcscheme │ ├── KeyboardInsetsExample.xcworkspace │ └── contents.xcworkspacedata │ ├── KeyboardInsetsExample │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ └── Contents.json │ ├── Base.lproj │ │ └── LaunchScreen.storyboard │ ├── Info.plist │ └── main.m │ ├── Podfile │ └── Podfile.lock ├── ios ├── KeyboardInsets.xcodeproj │ └── project.pbxproj └── KeyboardInsets │ ├── HBDKeyboardAutoHandler.h │ ├── HBDKeyboardAutoHandler.m │ ├── HBDKeyboardInsetsModule.h │ ├── HBDKeyboardInsetsModule.m │ ├── HBDKeyboardInsetsView.h │ ├── HBDKeyboardInsetsView.m │ ├── HBDKeyboardInsetsViewManager.h │ ├── HBDKeyboardInsetsViewManager.m │ ├── HBDKeyboardManualHandler.h │ └── HBDKeyboardManualHandler.m ├── metro.config.js ├── package.json ├── react-native.config.js ├── src ├── KeyboardInsetsView.tsx ├── hook.ts ├── index.ts └── native.ts ├── tsconfig.build.json ├── tsconfig.json └── yarn.lock /.eslintignore: -------------------------------------------------------------------------------- 1 | ios/ 2 | android/ 3 | builds/ 4 | */build/ 5 | lib/ 6 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | extends: ['@gfez/react-native', 'plugin:prettier/recommended', 'prettier/react'], 4 | } 5 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.pbxproj -text 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # Xcode 6 | # 7 | build/ 8 | *.pbxuser 9 | !default.pbxuser 10 | *.mode1v3 11 | !default.mode1v3 12 | *.mode2v3 13 | !default.mode2v3 14 | *.perspectivev3 15 | !default.perspectivev3 16 | xcuserdata 17 | *.xccheckout 18 | *.moved-aside 19 | DerivedData 20 | *.hmap 21 | *.ipa 22 | *.xcuserstate 23 | project.xcworkspace 24 | IDEWorkspaceChecks.plist 25 | 26 | # Android/IntelliJ 27 | # 28 | build/ 29 | .idea 30 | .gradle 31 | local.properties 32 | *.iml 33 | .project 34 | .settings/ 35 | 36 | # node.js 37 | # 38 | node_modules/ 39 | npm-debug.log 40 | yarn-error.log 41 | 42 | # BUCK 43 | buck-out/ 44 | \.buckd/ 45 | *.keystore 46 | !debug.keystore 47 | 48 | # fastlane 49 | # 50 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 51 | # screenshots whenever they are needed. 52 | # For more information about the recommended setup visit: 53 | # https://docs.fastlane.tools/best-practices/source-control/ 54 | 55 | */fastlane/report.xml 56 | */fastlane/Preview.html 57 | */fastlane/screenshots 58 | 59 | # Bundle artifact 60 | *.jsbundle 61 | 62 | # CocoaPods 63 | Pods/ 64 | 65 | # lib 66 | lib/ 67 | 68 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | semi: false, 3 | trailingComma: 'all', 4 | jsxBracketSameLine: true, 5 | singleQuote: true, 6 | printWidth: 120, 7 | tabWidth: 2, 8 | arrowParens: 'avoid' 9 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 listenzz@163.com 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-native-keyboard-insets 2 | 3 | 本库已迁移至 [react-native-troika/keyboard-insets](https://github.com/sdcxtech/react-native-troika/blob/master/packages/keyboard-insets/README.md) 4 | 5 | A powerful Keyboard Aware View for React Native. 6 | 7 | 使用简单,自动模式下不需要额外代码来处理键盘。 8 | 9 | | 自动模式 | 手动模式 | 10 | | ---------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- | 11 | | ![README-2023-02-02-15-56-36](https://todoit.oss-cn-shanghai.aliyuncs.com/assets/README-2023-02-02-15-56-36.gif) | ![README-2023-02-18-21-36-20](https://todoit.oss-cn-shanghai.aliyuncs.com/assets/README-2023-02-18-21-36-20.gif) | 12 | 13 | 本库主要依据 Android 官方指南 [Synchronize animation with the software keyboard](https://developer.android.com/develop/ui/views/layout/sw-keyboard#synchronize-animation) 来实现,同时参考了 [react-native-keyboard-controller](https://github.com/kirillzyusko/react-native-keyboard-controller)。因为该库不是很符合我的需求,所以我自己写了一个。 14 | 15 | ## Installation 16 | 17 | ```bash 18 | yarn add react-native-keyboard-insets 19 | ``` 20 | 21 | ### iOS 22 | 23 | ```sh 24 | cd ios 25 | pod install 26 | ``` 27 | 28 | ### Android 29 | 30 | 开启 [edge-to-edge](https://developer.android.com/develop/ui/views/layout/edge-to-edge)。 这将使得 APP 的 UI 撑满整个屏幕,而不是被系统 UI(譬如虚拟导航键)遮挡,从而实现更摩登的 UI 效果。 31 | 32 | ```java 33 | // MainActivity.java 34 | import androidx.core.view.WindowCompat; 35 | 36 | public class MainActivity extends ReactActivity { 37 | @Override 38 | protected void onCreate(Bundle savedInstanceState) { 39 | super.onCreate(null); 40 | // enable Edge-to-Edge 41 | WindowCompat.setDecorFitsSystemWindows(getWindow(), false); 42 | } 43 | } 44 | ``` 45 | 46 | 为了更好的向后(Android 10 以前)兼容, 在 AndroidManifest 中设置 `android:windowSoftInputMode="adjustResize"`。 47 | 48 | ```xml 49 | 50 | 54 | 55 | ... 56 | 57 | 58 | ``` 59 | 60 | 开启 Edge-to-Edge 后,你的 UI 会撑满整个屏幕,可使用 [react-native-safe-area-context](https://github.com/th3rdwave/react-native-safe-area-context) 来处理和系统 UI (譬如虚拟导航键) 重叠的部分。 61 | 62 | 可参考以下代码进行全局处理,也可以每个页面单独处理,以实现更美观更摩登的 UI 效果。 63 | 64 | ```tsx 65 | import { Platform } from 'react-native' 66 | import { SafeAreaProvider, SafeAreaView } from 'react-native-safe-area-context' 67 | 68 | function App() { 69 | return ( 70 | 71 | ... 72 | {Platform.OS === 'android' && } 73 | 74 | ) 75 | } 76 | ``` 77 | 78 | > 如果使用 [hybrid-navigation](https://github.com/listenzz/hybrid-navigation) 作为导航组件,则不需要做任何事情,因为它已经帮你处理好了。 79 | 80 | ## Usage 81 | 82 | 使用 `KeyboardInsetsView` 代替 `View` 作为容器,或者使用 `KeyboardInsetsView` 将 `ScrollView` 包裹起来。当键盘显示或隐藏时,`KeyboardInsetsView` 会自动调整自身的位置,以保证输入框不被键盘遮挡。 83 | 84 | ```tsx 85 | import { KeyboardInsetsView } from 'react-native-keyboard-insets' 86 | 87 | function MyComponent() { 88 | return ( 89 | 90 | 91 | ... 92 | 93 | ... 94 | 95 | 96 | ) 97 | } 98 | ``` 99 | 100 | Support Nested. 101 | 102 | ```tsx 103 | import { KeyboardInsetsView } from 'react-native-keyboard-insets' 104 | 105 | function MyComponent() { 106 | return ( 107 | 108 | ... 109 | 110 | 111 | 112 | ... 113 | 114 | ) 115 | } 116 | ``` 117 | 118 | `KeyboardInsetsView` 本质上是个 `View`,所以你可以使用 `View` 的所有属性,也可以和 `View` 互相替换。 119 | 120 | `KeyboardInsetsView` 有两个额外的属性: 121 | 122 | - `extraHeight`:自动模式下,键盘总是紧贴着输入框的下边缘,这个属性设置输入框距离键盘的额外高度。KeyboardInsetsView 的最大偏移受键盘高度限制,若加入额外高度后,KeyboardInsetsView 偏移距离大于键盘高度,将产生截断,此时 KeyboardInsetsView 偏移距离等于键盘高度,底部将与键盘顶部相贴 123 | 124 | - `onKeyboard`:是个回调函数,一旦设置,就进入手动模式,`KeyboardInsetsView` 不会帮你调整输入框的位置。你需要利用这个回调函数实现自己想要的效果。 125 | 126 | `onKeyboard` 的参数声明如下: 127 | 128 | ```tsx 129 | interface KeyboardState { 130 | height: number // 键盘的高度,不会因为键盘隐藏而变为 0 131 | shown: boolean // 当键盘将隐已隐时,这个值为 false;当键盘将显已显时,这个值为 true 132 | transitioning: boolean // 键盘是否正在显示或隐藏 133 | position: Animated.Value // 键盘的位置,从 0 到 height,可以用来实现动画效果 134 | } 135 | ``` 136 | 137 | ## API 138 | 139 | - `useKeyboard` 140 | 141 | 为了方便用户编写 `onKeyboard` 回调,keyboard-insets 提供了一个 `useKeyboard` hook,使用方法如下: 142 | 143 | ```tsx 144 | import { useKeyboard } from 'react-native-keyboard-insets' 145 | 146 | function MyComponent() { 147 | const { keyboard, onKeyboard } = useKeyboard() 148 | 149 | console.log(keyboard.height), // 键盘的高度 150 | 151 | return ( 152 | 153 | 154 | 155 | ) 156 | } 157 | ``` 158 | 159 | - `getEdgeInsetsForView` 160 | 161 | 有时候你需要知道某个 `View` 距离屏幕四边的距离,这个时候就可以使用 `getEdgeInsetsForView` 方法。 162 | 163 | ```tsx 164 | import { getEdgeInsetsForView } from 'react-native-keyboard-insets' 165 | 166 | function MyComponent() { 167 | const inputRef = useRef(null) 168 | 169 | const onLayout = useCallback(() => { 170 | const viewTag = findNodeHandle(inputRef.current) 171 | if (viewTag === null) { 172 | return 173 | } 174 | 175 | // 获得 TextInput 距离屏幕四边的距离 176 | getEdgeInsetsForView(viewTag, insets => { 177 | console.log('insets', insets) 178 | }) 179 | }, []) 180 | 181 | return ( 182 | 183 | 184 | 185 | ) 186 | } 187 | ``` 188 | 189 | ## 运行 example 项目 190 | 191 | 首先 clone 本项目 192 | 193 | ```shell 194 | git clone git@github.com:listenzz/react-native-keyboard-insets.git 195 | cd react-native-keyboard-insets 196 | ``` 197 | 198 | 然后在项目根目录下运行如下命令: 199 | 200 | ```shell 201 | yarn install 202 | # & 203 | yarn start 204 | ``` 205 | 206 | ### 在 Android 上运行 207 | 208 | 首先,确保你有一个模拟器或设备 209 | 210 | 如果熟悉原生开发,使用 Android Studio 打开 example/android,像运行原生应用那样运行它,也可以使用命令行: 211 | 212 | ```sh 213 | # 在项目根目录下运行 214 | yarn android 215 | ``` 216 | 217 | 你可能需要运行如下命令,才可以使用 Hot Reload 功能 218 | 219 | ```sh 220 | adb reverse tcp:8081 tcp:8081 221 | ``` 222 | 223 | ### 在 iOS 上运行 224 | 225 | 首先安装 cocoapods 依赖,在项目根目录下运行如下命令: 226 | 227 | ```sh 228 | cd example/ios && pod install 229 | # 成功安装依赖后,回到根目录 230 | cd - 231 | ``` 232 | 233 | 如果熟悉原生开发,使用 Xcode 打开 example/ios,像运行原生应用那样运行它,或者使用命令行: 234 | 235 | ```sh 236 | # 在项目根目录下运行 237 | yarn ios 238 | ``` 239 | -------------------------------------------------------------------------------- /RNKeyboardInsets.podspec: -------------------------------------------------------------------------------- 1 | require "json" 2 | 3 | package = JSON.parse(File.read(File.join(__dir__, "package.json"))) 4 | 5 | Pod::Spec.new do |s| 6 | s.name = "RNKeyboardInsets" 7 | s.version = package["version"] 8 | s.summary = package["description"] 9 | 10 | s.homepage = package["homepage"] 11 | s.license = package["license"] 12 | s.authors = package["author"] 13 | s.platforms = { :ios => "10.0" } 14 | s.source = { :git => "https://github.com/github-account/react-native-keyboard-insets.git", :tag => "#{s.version}" } 15 | 16 | s.source_files = "ios/KeyboardInsets/**/*.{h,m,mm}" 17 | s.dependency "React-Core" 18 | end -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | // android/build.gradle 2 | 3 | def safeExtGet(prop, fallback) { 4 | rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback 5 | } 6 | 7 | apply plugin: 'com.android.library' 8 | 9 | android { 10 | compileOptions { 11 | sourceCompatibility JavaVersion.VERSION_1_8 12 | targetCompatibility JavaVersion.VERSION_1_8 13 | } 14 | 15 | compileSdkVersion safeExtGet('compileSdkVersion', 30) 16 | buildToolsVersion safeExtGet('buildToolsVersion', '30.0.2') 17 | 18 | defaultConfig { 19 | minSdkVersion safeExtGet('minSdkVersion', 21) 20 | targetSdkVersion safeExtGet('targetSdkVersion', 30) 21 | versionCode 1 22 | versionName "1.0.0" 23 | } 24 | 25 | buildTypes { 26 | release { 27 | consumerProguardFiles 'proguard-rules.pro' 28 | } 29 | } 30 | } 31 | 32 | dependencies { 33 | implementation fileTree(dir: 'libs', include: ['*.jar']) 34 | implementation "androidx.appcompat:appcompat:1.3.1" 35 | implementation 'com.facebook.react:react-native:+' 36 | } 37 | -------------------------------------------------------------------------------- /android/proguard-rules.pro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/listenzz/react-native-keyboard-insets/7bc81ed8f6434afc4321b71fbf33cfcff20fbde4/android/proguard-rules.pro -------------------------------------------------------------------------------- /android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /android/src/main/java/com/reactnative/keyboardinsets/EdgeInsets.java: -------------------------------------------------------------------------------- 1 | package com.reactnative.keyboardinsets; 2 | 3 | public class EdgeInsets { 4 | public int left; 5 | public int top; 6 | public int right; 7 | public int bottom; 8 | 9 | public EdgeInsets() { 10 | 11 | } 12 | 13 | public EdgeInsets(int left, int top, int right, int bottom) { 14 | this.left = left; 15 | this.top = top; 16 | this.right = right; 17 | this.bottom = bottom; 18 | } 19 | 20 | @Override 21 | public String toString() { 22 | return "EdgeInsets{" + 23 | "left=" + left + 24 | ", top=" + top + 25 | ", right=" + right + 26 | ", bottom=" + bottom + 27 | '}'; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /android/src/main/java/com/reactnative/keyboardinsets/KeyboardAutoHandler.java: -------------------------------------------------------------------------------- 1 | package com.reactnative.keyboardinsets; 2 | 3 | import android.graphics.Rect; 4 | import android.view.View; 5 | import android.view.ViewParent; 6 | 7 | import androidx.core.graphics.Insets; 8 | import androidx.core.view.WindowInsetsCompat; 9 | 10 | import com.facebook.react.uimanager.PixelUtil; 11 | import com.facebook.react.uimanager.ThemedReactContext; 12 | import com.facebook.react.views.scroll.ReactScrollView; 13 | 14 | public class KeyboardAutoHandler { 15 | 16 | private final KeyboardInsetsView view; 17 | private final ThemedReactContext reactContext; 18 | 19 | public KeyboardAutoHandler(KeyboardInsetsView view, ThemedReactContext reactContext) { 20 | this.view = view; 21 | this.reactContext = reactContext; 22 | } 23 | 24 | private int keyboardHeight; 25 | private boolean forceUpdated = false; 26 | 27 | void onStart(View focusView, int keyboardHeight) { 28 | adjustScrollViewOffsetIfNeeded(focusView); 29 | 30 | if (keyboardHeight != this.keyboardHeight) { 31 | forceUpdated = true; 32 | } 33 | this.keyboardHeight = keyboardHeight; 34 | } 35 | 36 | void onEnd(View focusView, int keyboardHeight) { 37 | 38 | } 39 | 40 | void onApplyWindowInsets(WindowInsetsCompat insets, View focusView, int keyboardHeight) { 41 | if (focusView == null) { 42 | view.setTranslationY(0); 43 | return; 44 | } 45 | 46 | adjustScrollViewOffsetIfNeeded(focusView); 47 | 48 | if (keyboardHeight != this.keyboardHeight) { 49 | forceUpdated = true; 50 | } 51 | this.keyboardHeight = keyboardHeight; 52 | 53 | handleKeyboardTransition(insets, focusView); 54 | } 55 | 56 | void handleKeyboardTransition(WindowInsetsCompat insets, View focusView) { 57 | Insets imeInsets = insets.getInsets(WindowInsetsCompat.Type.ime()); 58 | EdgeInsets edge = SystemUI.getEdgeInsetsForView(focusView); 59 | float extraHeight = PixelUtil.toPixelFromDIP(view.getExtraHeight()); 60 | float translationY = 0; 61 | if (imeInsets.bottom > 0) { 62 | float bottomInset = Math.max(edge.bottom - extraHeight, 0); 63 | translationY = -Math.max(imeInsets.bottom - bottomInset, 0); 64 | } 65 | 66 | if (forceUpdated) { 67 | forceUpdated = false; 68 | view.setTranslationY(translationY); 69 | } 70 | 71 | if (SystemUI.isImeVisible(view) && view.getTranslationY() < translationY) { 72 | return; 73 | } 74 | 75 | view.setTranslationY(translationY); 76 | } 77 | 78 | private void adjustScrollViewOffsetIfNeeded(View focusView) { 79 | ReactScrollView scrollView = findClosestScrollView(focusView); 80 | if (scrollView != null) { 81 | Rect offset = new Rect(); 82 | focusView.getDrawingRect(offset); 83 | scrollView.offsetDescendantRectToMyCoords(focusView, offset); 84 | float extraHeight = PixelUtil.toPixelFromDIP(view.getExtraHeight()); 85 | float dy = scrollView.getHeight() + scrollView.getScrollY() - offset.bottom - extraHeight; 86 | if (dy < 0) { 87 | scrollView.scrollTo(0, (int) (scrollView.getScrollY() - dy)); 88 | scrollView.requestLayout(); 89 | } 90 | } 91 | } 92 | 93 | private static ReactScrollView findClosestScrollView(View view) { 94 | ViewParent viewParent = view.getParent(); 95 | if (viewParent instanceof ReactScrollView) { 96 | return (ReactScrollView) viewParent; 97 | } 98 | 99 | if (viewParent instanceof View) { 100 | return findClosestScrollView((View) viewParent); 101 | } 102 | 103 | return null; 104 | } 105 | 106 | } 107 | -------------------------------------------------------------------------------- /android/src/main/java/com/reactnative/keyboardinsets/KeyboardInsetsCallback.java: -------------------------------------------------------------------------------- 1 | package com.reactnative.keyboardinsets; 2 | 3 | import android.view.View; 4 | import android.view.ViewParent; 5 | 6 | import androidx.annotation.NonNull; 7 | import androidx.core.graphics.Insets; 8 | import androidx.core.view.OnApplyWindowInsetsListener; 9 | import androidx.core.view.WindowInsetsAnimationCompat; 10 | import androidx.core.view.WindowInsetsCompat; 11 | 12 | import com.facebook.common.logging.FLog; 13 | import com.facebook.react.uimanager.ThemedReactContext; 14 | 15 | import java.util.List; 16 | 17 | public class KeyboardInsetsCallback extends WindowInsetsAnimationCompat.Callback implements OnApplyWindowInsetsListener { 18 | 19 | private final KeyboardInsetsView view; 20 | private final KeyboardAutoHandler autoHandler; 21 | private final KeyboardManualHandler manualHandler; 22 | 23 | public KeyboardInsetsCallback(KeyboardInsetsView view, ThemedReactContext reactContext) { 24 | super(WindowInsetsAnimationCompat.Callback.DISPATCH_MODE_CONTINUE_ON_SUBTREE); 25 | this.view = view; 26 | this.autoHandler = new KeyboardAutoHandler(view, reactContext); 27 | this.manualHandler = new KeyboardManualHandler(view, reactContext); 28 | } 29 | 30 | public View focusView; 31 | private boolean transitioning; 32 | private int keyboardHeight; 33 | 34 | @Override 35 | public void onPrepare(@NonNull WindowInsetsAnimationCompat animation) { 36 | transitioning = true; 37 | } 38 | 39 | @NonNull 40 | @Override 41 | public WindowInsetsAnimationCompat.BoundsCompat onStart(@NonNull WindowInsetsAnimationCompat animation, @NonNull WindowInsetsAnimationCompat.BoundsCompat bounds) { 42 | if (SystemUI.isImeVisible(view)) { 43 | focusView = view.findFocus(); 44 | } 45 | 46 | if (!shouldHandleKeyboardTransition(focusView)) { 47 | return super.onStart(animation, bounds); 48 | } 49 | 50 | if (SystemUI.isImeVisible(view)) { 51 | keyboardHeight = SystemUI.imeHeight(view); 52 | } 53 | 54 | FLog.i("KeyboardInsets", "WindowInsetsAnimation.Callback onStart"); 55 | if (view.isAutoMode()) { 56 | autoHandler.onStart(focusView, keyboardHeight); 57 | } else { 58 | manualHandler.onStart(focusView, keyboardHeight); 59 | } 60 | 61 | return super.onStart(animation, bounds); 62 | } 63 | 64 | @Override 65 | public void onEnd(@NonNull WindowInsetsAnimationCompat animation) { 66 | super.onEnd(animation); 67 | transitioning = false; 68 | 69 | if (!shouldHandleKeyboardTransition(focusView)) { 70 | return; 71 | } 72 | 73 | if (!SystemUI.isImeVisible(view)) { 74 | focusView = null; 75 | } 76 | 77 | FLog.i("KeyboardInsets", "WindowInsetsAnimation.Callback onEnd"); 78 | if (view.isAutoMode()) { 79 | autoHandler.onEnd(focusView, keyboardHeight); 80 | } else { 81 | manualHandler.onEnd(focusView, keyboardHeight); 82 | } 83 | } 84 | 85 | @NonNull 86 | @Override 87 | public WindowInsetsCompat onProgress(@NonNull WindowInsetsCompat insets, @NonNull List runningAnimations) { 88 | if (!shouldHandleKeyboardTransition(focusView)) { 89 | return insets; 90 | } 91 | handleKeyboardTransition(insets); 92 | return WindowInsetsCompat.CONSUMED; 93 | } 94 | 95 | @Override 96 | public WindowInsetsCompat onApplyWindowInsets(View v, WindowInsetsCompat insets) { 97 | if (transitioning) { 98 | return insets; 99 | } 100 | 101 | if (focusView == null) { 102 | // Android 10 以下,首次弹出键盘时,不会触发 WindowInsetsAnimationCompat.Callback 103 | focusView = view.findFocus(); 104 | } 105 | 106 | if (!shouldHandleKeyboardTransition(focusView)) { 107 | return insets; 108 | } 109 | 110 | Insets imeInsets = insets.getInsets(WindowInsetsCompat.Type.ime()); 111 | FLog.i("KeyboardInsets", "onApplyWindowInsets imeInsets" + imeInsets); 112 | 113 | if (SystemUI.isImeVisible(view)) { 114 | keyboardHeight = SystemUI.imeHeight(view); 115 | } 116 | 117 | if (view.isAutoMode()) { 118 | View focusView = view.findFocus(); 119 | if (focusView != null && focusView != this.focusView) { 120 | KeyboardInsetsView insetsView = findClosestKeyboardInsetsView(focusView); 121 | if (insetsView != view) { 122 | focusView = null; 123 | } 124 | } 125 | this.focusView = focusView; 126 | autoHandler.onApplyWindowInsets(insets, focusView, keyboardHeight); 127 | } else { 128 | manualHandler.onApplyWindowInsets(insets, focusView, keyboardHeight); 129 | } 130 | 131 | return insets; 132 | } 133 | 134 | private void handleKeyboardTransition(WindowInsetsCompat insets) { 135 | if (view.isAutoMode()) { 136 | autoHandler.handleKeyboardTransition(insets, focusView); 137 | } else { 138 | manualHandler.handleKeyboardTransition(insets, focusView); 139 | } 140 | } 141 | 142 | private boolean shouldHandleKeyboardTransition(View focus) { 143 | if (focus != null) { 144 | KeyboardInsetsView insetsView = findClosestKeyboardInsetsView(focus); 145 | return insetsView == view; 146 | } 147 | return false; 148 | } 149 | 150 | private static KeyboardInsetsView findClosestKeyboardInsetsView(View focus) { 151 | ViewParent viewParent = focus.getParent(); 152 | if (viewParent instanceof KeyboardInsetsView) { 153 | return (KeyboardInsetsView) viewParent; 154 | } 155 | 156 | if (viewParent instanceof View) { 157 | return findClosestKeyboardInsetsView((View) viewParent); 158 | } 159 | 160 | return null; 161 | } 162 | 163 | } 164 | -------------------------------------------------------------------------------- /android/src/main/java/com/reactnative/keyboardinsets/KeyboardInsetsModule.java: -------------------------------------------------------------------------------- 1 | package com.reactnative.keyboardinsets; 2 | 3 | import android.view.View; 4 | 5 | import androidx.annotation.NonNull; 6 | 7 | import com.facebook.react.bridge.Arguments; 8 | import com.facebook.react.bridge.ReactApplicationContext; 9 | import com.facebook.react.bridge.ReactContextBaseJavaModule; 10 | import com.facebook.react.bridge.ReactMethod; 11 | import com.facebook.react.bridge.Callback; 12 | import com.facebook.react.bridge.UiThreadUtil; 13 | import com.facebook.react.bridge.WritableMap; 14 | import com.facebook.react.uimanager.PixelUtil; 15 | import com.facebook.react.uimanager.UIManagerModule; 16 | 17 | public class KeyboardInsetsModule extends ReactContextBaseJavaModule { 18 | 19 | public KeyboardInsetsModule(ReactApplicationContext reactContext) { 20 | super(reactContext); 21 | } 22 | 23 | @NonNull 24 | @Override 25 | public String getName() { 26 | return "KeyboardInsetsModule"; 27 | } 28 | 29 | @ReactMethod 30 | public void getEdgeInsetsForView(int viewTag, Callback callback) { 31 | UiThreadUtil.runOnUiThread(() -> { 32 | UIManagerModule uiManagerModule = getReactApplicationContext().getNativeModule(UIManagerModule.class); 33 | View view = uiManagerModule.resolveView(viewTag); 34 | WritableMap map = Arguments.createMap(); 35 | map.putDouble("top", 0); 36 | map.putDouble("left", 0); 37 | map.putDouble("right", 0); 38 | map.putDouble("bottom", 0); 39 | if (view != null) { 40 | EdgeInsets insets = SystemUI.getEdgeInsetsForView(view); 41 | map.putDouble("top", PixelUtil.toDIPFromPixel(insets.top)); 42 | map.putDouble("left", PixelUtil.toDIPFromPixel(insets.left)); 43 | map.putDouble("right", PixelUtil.toDIPFromPixel(insets.right)); 44 | map.putDouble("bottom", PixelUtil.toDIPFromPixel(insets.bottom)); 45 | } 46 | callback.invoke(map); 47 | }); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /android/src/main/java/com/reactnative/keyboardinsets/KeyboardInsetsPackage.java: -------------------------------------------------------------------------------- 1 | package com.reactnative.keyboardinsets; 2 | 3 | import androidx.annotation.NonNull; 4 | 5 | import java.util.Collections; 6 | import java.util.List; 7 | 8 | import com.facebook.react.ReactPackage; 9 | import com.facebook.react.bridge.NativeModule; 10 | import com.facebook.react.bridge.ReactApplicationContext; 11 | import com.facebook.react.uimanager.ViewManager; 12 | 13 | public class KeyboardInsetsPackage implements ReactPackage { 14 | @NonNull 15 | @Override 16 | public List createNativeModules(@NonNull ReactApplicationContext reactContext) { 17 | return Collections.singletonList(new KeyboardInsetsModule(reactContext)); 18 | } 19 | 20 | @NonNull 21 | @Override 22 | public List createViewManagers(@NonNull ReactApplicationContext reactContext) { 23 | return Collections.singletonList(new KeyboardInsetsViewManager()); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /android/src/main/java/com/reactnative/keyboardinsets/KeyboardInsetsView.java: -------------------------------------------------------------------------------- 1 | package com.reactnative.keyboardinsets; 2 | 3 | import android.content.Context; 4 | import android.view.View; 5 | 6 | import com.facebook.react.views.view.ReactViewGroup; 7 | 8 | public class KeyboardInsetsView extends ReactViewGroup { 9 | 10 | private String mode = "auto"; 11 | 12 | private float extraHeight = 0.0f; 13 | 14 | public KeyboardInsetsView(Context context) { 15 | super(context); 16 | } 17 | 18 | public void setMode(String mode) { 19 | this.mode = mode; 20 | } 21 | 22 | public boolean isAutoMode() { 23 | return this.mode.equals("auto"); 24 | } 25 | 26 | public void setExtraHeight(float extraHeight) { 27 | this.extraHeight = extraHeight; 28 | } 29 | 30 | public float getExtraHeight() { 31 | return this.extraHeight; 32 | } 33 | 34 | 35 | @Override 36 | public void requestChildFocus(View child, View focused) { 37 | super.requestChildFocus(child, focused); 38 | requestApplyInsets(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /android/src/main/java/com/reactnative/keyboardinsets/KeyboardInsetsViewManager.java: -------------------------------------------------------------------------------- 1 | package com.reactnative.keyboardinsets; 2 | 3 | import androidx.annotation.NonNull; 4 | import androidx.annotation.Nullable; 5 | import androidx.core.view.ViewCompat; 6 | 7 | import com.facebook.react.common.MapBuilder; 8 | import com.facebook.react.uimanager.ThemedReactContext; 9 | import com.facebook.react.uimanager.ViewGroupManager; 10 | import com.facebook.react.uimanager.annotations.ReactProp; 11 | 12 | import java.util.Map; 13 | 14 | public class KeyboardInsetsViewManager extends ViewGroupManager { 15 | 16 | public static final String REACT_CLASS = "KeyboardInsetsView"; 17 | 18 | @NonNull 19 | @Override 20 | public String getName() { 21 | return REACT_CLASS; 22 | } 23 | 24 | @NonNull 25 | @Override 26 | protected KeyboardInsetsView createViewInstance(@NonNull ThemedReactContext themedReactContext) { 27 | KeyboardInsetsView view = new KeyboardInsetsView(themedReactContext); 28 | KeyboardInsetsCallback callback = new KeyboardInsetsCallback(view, themedReactContext); 29 | ViewCompat.setWindowInsetsAnimationCallback(view, callback); 30 | ViewCompat.setOnApplyWindowInsetsListener(view, callback); 31 | return view; 32 | } 33 | 34 | @ReactProp(name = "mode") 35 | public void setMode(KeyboardInsetsView view, String mode) { 36 | view.setMode(mode); 37 | } 38 | 39 | @ReactProp(name = "extraHeight") 40 | public void setExtraHeight(KeyboardInsetsView view, float extraHeight) { 41 | view.setExtraHeight(extraHeight); 42 | } 43 | 44 | @Nullable 45 | @Override 46 | public Map getExportedCustomDirectEventTypeConstants() { 47 | return MapBuilder.of( 48 | "topStatusChanged", MapBuilder.of("registrationName", "onStatusChanged"), 49 | "topPositionChanged", MapBuilder.of("registrationName", "onPositionChanged") 50 | ); 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /android/src/main/java/com/reactnative/keyboardinsets/KeyboardManualHandler.java: -------------------------------------------------------------------------------- 1 | package com.reactnative.keyboardinsets; 2 | 3 | import android.util.Log; 4 | import android.view.View; 5 | 6 | import androidx.core.graphics.Insets; 7 | import androidx.core.view.WindowInsetsCompat; 8 | 9 | import com.facebook.react.uimanager.ThemedReactContext; 10 | import com.facebook.react.uimanager.UIManagerHelper; 11 | import com.facebook.react.uimanager.events.Event; 12 | import com.facebook.react.uimanager.events.EventDispatcher; 13 | 14 | public class KeyboardManualHandler { 15 | 16 | private final KeyboardInsetsView view; 17 | private final ThemedReactContext reactContext; 18 | 19 | public KeyboardManualHandler(KeyboardInsetsView view, ThemedReactContext reactContext) { 20 | this.view = view; 21 | this.reactContext = reactContext; 22 | } 23 | 24 | void onStart(View focusView, int keyboardHeight) { 25 | sendEvent(new KeyboardStatusChangedEvent(view.getId(), keyboardHeight, SystemUI.isImeVisible(view), true)); 26 | } 27 | 28 | void onEnd(View focusView, int keyboardHeight) { 29 | sendEvent(new KeyboardStatusChangedEvent(view.getId(), keyboardHeight, SystemUI.isImeVisible(view), false)); 30 | } 31 | 32 | void onApplyWindowInsets(WindowInsetsCompat insets, View focusView, int keyboardHeight) { 33 | sendEvent(new KeyboardStatusChangedEvent(view.getId(), keyboardHeight, SystemUI.isImeVisible(view), true)); 34 | handleKeyboardTransition(insets, focusView); 35 | sendEvent(new KeyboardStatusChangedEvent(view.getId(), keyboardHeight, SystemUI.isImeVisible(view), false)); 36 | } 37 | 38 | void handleKeyboardTransition(WindowInsetsCompat insets, View focusView) { 39 | Insets imeInsets = insets.getInsets(WindowInsetsCompat.Type.ime()); 40 | Log.d("KeyboardInsets", "imeInsets.bottom:" + imeInsets.bottom); 41 | sendEvent(new KeyboardPositionChangedEvent(view.getId(), imeInsets.bottom)); 42 | } 43 | 44 | private void sendEvent(Event event) { 45 | EventDispatcher eventDispatcher = UIManagerHelper.getEventDispatcherForReactTag(reactContext, view.getId()); 46 | eventDispatcher.dispatchEvent(event); 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /android/src/main/java/com/reactnative/keyboardinsets/KeyboardPositionChangedEvent.java: -------------------------------------------------------------------------------- 1 | package com.reactnative.keyboardinsets; 2 | 3 | import androidx.core.graphics.Insets; 4 | 5 | import com.facebook.react.bridge.Arguments; 6 | import com.facebook.react.bridge.WritableMap; 7 | import com.facebook.react.uimanager.PixelUtil; 8 | import com.facebook.react.uimanager.events.Event; 9 | import com.facebook.react.uimanager.events.RCTEventEmitter; 10 | 11 | public class KeyboardPositionChangedEvent extends Event { 12 | 13 | private final int position; 14 | 15 | public KeyboardPositionChangedEvent(int viewId, int position) { 16 | super(viewId); 17 | this.position = position; 18 | } 19 | 20 | @Override 21 | public String getEventName() { 22 | return "topPositionChanged"; 23 | } 24 | 25 | @Override 26 | public void dispatch(RCTEventEmitter rctEventEmitter) { 27 | WritableMap map = Arguments.createMap(); 28 | map.putDouble("position", PixelUtil.toDIPFromPixel(position)); 29 | rctEventEmitter.receiveEvent(getViewTag(), getEventName(), map); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /android/src/main/java/com/reactnative/keyboardinsets/KeyboardStatusChangedEvent.java: -------------------------------------------------------------------------------- 1 | package com.reactnative.keyboardinsets; 2 | 3 | import com.facebook.react.bridge.Arguments; 4 | import com.facebook.react.bridge.WritableMap; 5 | import com.facebook.react.uimanager.PixelUtil; 6 | import com.facebook.react.uimanager.events.Event; 7 | import com.facebook.react.uimanager.events.RCTEventEmitter; 8 | 9 | public class KeyboardStatusChangedEvent extends Event { 10 | 11 | private final int height; 12 | private final boolean shown; 13 | private final boolean transitioning; 14 | 15 | public KeyboardStatusChangedEvent(int viewTag, int height, boolean shown, boolean transitioning) { 16 | super(viewTag); 17 | this.height = height; 18 | this.shown = shown; 19 | this.transitioning = transitioning; 20 | } 21 | 22 | @Override 23 | public String getEventName() { 24 | return "topStatusChanged"; 25 | } 26 | 27 | @Override 28 | public void dispatch(RCTEventEmitter rctEventEmitter) { 29 | WritableMap map = Arguments.createMap(); 30 | map.putDouble("height", PixelUtil.toDIPFromPixel(height)); 31 | map.putBoolean("transitioning", transitioning); 32 | map.putBoolean("shown", shown); 33 | rctEventEmitter.receiveEvent(getViewTag(), getEventName(), map); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /android/src/main/java/com/reactnative/keyboardinsets/SystemUI.java: -------------------------------------------------------------------------------- 1 | package com.reactnative.keyboardinsets; 2 | 3 | import android.graphics.Rect; 4 | import android.view.View; 5 | import android.view.ViewGroup; 6 | 7 | import androidx.annotation.NonNull; 8 | import androidx.core.view.ViewCompat; 9 | import androidx.core.view.WindowInsetsCompat; 10 | 11 | public class SystemUI { 12 | 13 | public static boolean isImeVisible(@NonNull View view) { 14 | WindowInsetsCompat insetsCompat = ViewCompat.getRootWindowInsets(view); 15 | assert insetsCompat != null; 16 | return insetsCompat.isVisible(WindowInsetsCompat.Type.ime()); 17 | } 18 | 19 | public static int imeHeight(@NonNull View view) { 20 | WindowInsetsCompat insetsCompat = ViewCompat.getRootWindowInsets(view); 21 | assert insetsCompat != null; 22 | return insetsCompat.getInsets(WindowInsetsCompat.Type.ime()).bottom; 23 | } 24 | 25 | public static EdgeInsets getEdgeInsetsForView(@NonNull View view) { 26 | ViewGroup root = (ViewGroup) view.getRootView(); 27 | 28 | int windowHeight = root.getHeight(); 29 | int windowWidth = root.getWidth(); 30 | 31 | Rect offset = new Rect(); 32 | view.getDrawingRect(offset); 33 | root.offsetDescendantRectToMyCoords(view, offset); 34 | 35 | int leftMargin = 0; 36 | int topMargin = 0; 37 | int rightMargin = 0; 38 | int bottomMargin = 0; 39 | 40 | if (view.getLayoutParams() instanceof ViewGroup.MarginLayoutParams) { 41 | ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) view.getLayoutParams(); 42 | leftMargin = lp.leftMargin; 43 | topMargin = lp.topMargin; 44 | rightMargin = lp.rightMargin; 45 | bottomMargin = lp.bottomMargin; 46 | } 47 | 48 | EdgeInsets insets = new EdgeInsets(); 49 | insets.left = Math.max(offset.left - leftMargin, 0); 50 | insets.top = Math.max(offset.top - topMargin, 0); 51 | insets.right = Math.max(windowWidth - offset.right - rightMargin, 0); 52 | insets.bottom = Math.max(windowHeight - offset.bottom - bottomMargin, 0); 53 | return insets; 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ['module:metro-react-native-babel-preset'], 3 | } 4 | -------------------------------------------------------------------------------- /example/App.tsx: -------------------------------------------------------------------------------- 1 | import React, { Fragment, useRef } from 'react' 2 | import { withNavigationItem } from 'hybrid-navigation' 3 | import { StyleSheet, TextInput, ScrollView } from 'react-native' 4 | import { KeyboardInsetsView } from 'react-native-keyboard-insets' 5 | import { SafeAreaProvider, SafeAreaView } from 'react-native-safe-area-context' 6 | 7 | function App() { 8 | return ( 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | {Array.from({ length: 10 }).map((item, index) => ( 17 | 24 | ))} 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | ) 33 | } 34 | 35 | const inputLength = 9 36 | function SubmitToNextInputFragment() { 37 | const inputRef = useRef<(TextInput | null)[]>([...Array(inputLength)]) 38 | const goNextInput = (index: number) => { 39 | if (index !== inputLength - 1) { 40 | inputRef.current[index + 1]?.focus() 41 | } 42 | } 43 | return ( 44 | 45 | {Array.from({ length: inputLength }).map((_, index) => ( 46 | (inputRef.current[index] = ref)} 48 | key={index} 49 | style={styles.input} 50 | placeholder={index === inputLength - 1 ? 'submit' : `current:${index} => submit and next`} 51 | textAlignVertical="center" 52 | blurOnSubmit={index === inputLength - 1} 53 | autoCorrect={false} 54 | returnKeyType={index === inputLength - 1 ? 'done' : 'next'} 55 | onSubmitEditing={() => goNextInput(index)} 56 | /> 57 | ))} 58 | 59 | ) 60 | } 61 | 62 | export default withNavigationItem({ 63 | titleItem: { 64 | title: 'KeyboardInsets 演示', 65 | }, 66 | rightBarButtonItem: { 67 | title: 'chat', 68 | action: navigator => { 69 | navigator.push('Chat') 70 | }, 71 | }, 72 | })(App) 73 | 74 | const styles = StyleSheet.create({ 75 | flex1: { 76 | flex: 1, 77 | }, 78 | backgroundLime: { 79 | backgroundColor: 'lime', 80 | }, 81 | container: { 82 | justifyContent: 'flex-start', 83 | alignItems: 'stretch', 84 | paddingBottom: 16, 85 | }, 86 | 87 | input: { 88 | height: 100, 89 | marginHorizontal: 48, 90 | marginTop: 16, 91 | marginBottom: 0, 92 | paddingLeft: 8, 93 | paddingRight: 8, 94 | borderColor: '#cccccc', 95 | borderWidth: 1, 96 | }, 97 | 98 | keyboard: { 99 | paddingBottom: 16, 100 | backgroundColor: 'bisque', 101 | }, 102 | }) 103 | -------------------------------------------------------------------------------- /example/KeyboardChat/Message/data.ts: -------------------------------------------------------------------------------- 1 | import type { MessageProps } from './types' 2 | 3 | export const history: MessageProps[] = [ 4 | { text: 'Hmmmm🤔' }, 5 | { text: 'It looks like it still will be laggy...' }, 6 | { text: "But I don't know what should I try next" }, 7 | { text: 'Reanimated?', sender: true }, 8 | { text: 'A little bit disappointed 😔' }, 9 | { text: '🤯' }, 10 | { text: 'Try to check it. I hope it helps you...', sender: true }, 11 | { text: 'It really pushes you to think twice on how to design it first' }, 12 | { 13 | text: 'Looks promising!😎 I was always looking for a solution that would allow us to run animations on native thread and provide at least stable 60 FPS', 14 | }, 15 | { text: 'You have to check it!!!', sender: true }, 16 | { text: "Ha-ha! I'm definitely going to check it!" }, 17 | { text: 'Hello! How are you?' }, 18 | { text: "Hi! I'm good. How are you?", sender: true }, 19 | { 20 | text: "I'm fine, thank you! Have you seen new keyboard animation library?", 21 | }, 22 | { text: 'No! Let me check.', sender: true }, 23 | { 24 | text: "Wow! I've been looking for it for a while. It's awesome!", 25 | sender: true, 26 | }, 27 | ] 28 | -------------------------------------------------------------------------------- /example/KeyboardChat/Message/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Text, View } from 'react-native' 3 | import styles from './styles' 4 | 5 | import type { MessageProps } from './types' 6 | 7 | export default function Message({ text, sender }: MessageProps) { 8 | return ( 9 | 10 | {text} 11 | 12 | ) 13 | } 14 | -------------------------------------------------------------------------------- /example/KeyboardChat/Message/styles.ts: -------------------------------------------------------------------------------- 1 | import { StyleSheet } from 'react-native' 2 | 3 | const container = { 4 | borderRadius: 10, 5 | padding: 10, 6 | margin: 10, 7 | marginVertical: 5, 8 | } 9 | export default StyleSheet.create({ 10 | senderContainer: { 11 | alignSelf: 'flex-end', 12 | backgroundColor: '#e0e0e0', 13 | ...container, 14 | }, 15 | recipientContainer: { 16 | alignSelf: 'flex-start', 17 | backgroundColor: '#50FF00', 18 | ...container, 19 | }, 20 | message: { 21 | color: '#000000', 22 | }, 23 | }) 24 | -------------------------------------------------------------------------------- /example/KeyboardChat/Message/types.ts: -------------------------------------------------------------------------------- 1 | export type MessageProps = { 2 | text: string 3 | sender?: boolean 4 | } 5 | -------------------------------------------------------------------------------- /example/KeyboardChat/driver/Driver.ts: -------------------------------------------------------------------------------- 1 | import { Animated } from 'react-native' 2 | 3 | export interface DriverState { 4 | // sender bottom 5 | bottom: number 6 | driver: Driver | undefined 7 | setDriver: React.Dispatch> 8 | setTranslateY: React.Dispatch> 9 | } 10 | 11 | export interface Driver { 12 | show: (state: DriverState) => void 13 | hide: (state: DriverState) => void 14 | toggle: (state: DriverState) => void 15 | shown: boolean 16 | height: number 17 | name: string 18 | } 19 | -------------------------------------------------------------------------------- /example/KeyboardChat/driver/KeyboardDriver.ts: -------------------------------------------------------------------------------- 1 | import { TextInput, Keyboard, Animated } from 'react-native' 2 | import { KeyboardState } from 'react-native-keyboard-insets' 3 | import { Driver, DriverState } from './Driver' 4 | 5 | export class KeyboardDriver implements Driver { 6 | constructor(private inputRef: React.RefObject) {} 7 | // 输入框距屏幕底部的距离 8 | private senderBottom = 0 9 | private y = 0 10 | private position = new Animated.Value(0) 11 | 12 | name = 'keyboard' 13 | shown = false 14 | height = 0 15 | 16 | show = () => { 17 | this.inputRef.current?.focus() 18 | } 19 | 20 | hide = () => { 21 | Keyboard.dismiss() 22 | } 23 | 24 | toggle = () => { 25 | this.shown ? this.hide() : this.show() 26 | } 27 | 28 | createCallback = (state: DriverState) => { 29 | return (keyboard: KeyboardState) => { 30 | const { shown, height, position } = keyboard 31 | 32 | const { bottom, driver, setDriver, setTranslateY } = state 33 | const heightChanged = height !== this.height 34 | 35 | this.height = height 36 | this.position = position 37 | this.senderBottom = bottom 38 | 39 | if (shown && (!this.shown || heightChanged)) { 40 | this.shown = true 41 | if (driver && driver !== this) { 42 | // 记录主界面当前位置 43 | this.y = driver.shown ? driver.height : 0 44 | // 隐藏前一个 driver 45 | driver.hide({ bottom, driver: this, setDriver, setTranslateY }) 46 | } 47 | setDriver(this) 48 | setTranslateY(this.translateY) 49 | } 50 | 51 | if (!shown && this.shown) { 52 | this.shown = false 53 | this.y = 0 54 | if (driver === this) { 55 | setDriver(undefined) 56 | setTranslateY(this.translateY) 57 | } 58 | } 59 | } 60 | } 61 | 62 | private get translateY() { 63 | const extraHeight = this.senderBottom 64 | console.log(this.name, 'height', this.height, 'y', this.y, 'extraHeight', extraHeight) 65 | 66 | if (!this.shown || this.y === 0) { 67 | return this.position.interpolate({ 68 | inputRange: [extraHeight, this.height], 69 | outputRange: [0, extraHeight - this.height], 70 | extrapolate: 'clamp', 71 | }) as Animated.Value 72 | } else { 73 | return this.position.interpolate({ 74 | inputRange: [0, this.height], 75 | outputRange: [extraHeight - this.y, extraHeight - this.height], 76 | extrapolate: 'clamp', 77 | }) as Animated.Value 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /example/KeyboardChat/driver/ViewDriver.ts: -------------------------------------------------------------------------------- 1 | import { Animated, LayoutChangeEvent } from 'react-native' 2 | import { Driver, DriverState } from './Driver' 3 | 4 | export class ViewDriver implements Driver { 5 | constructor(public name: string) {} 6 | 7 | // 输入框距屏幕底部的距离 8 | private senderBottom = 0 9 | 10 | private y = 0 11 | private animation = new Animated.Value(0) 12 | 13 | shown = false 14 | height = 0 15 | 16 | show = (state: DriverState) => { 17 | const { bottom, driver, setDriver, setTranslateY } = state 18 | 19 | if (driver && driver !== this) { 20 | // 记录主界面当前位置 21 | this.y = driver.shown ? driver.height : 0 22 | // 隐藏前一个 driver 23 | driver.hide({ bottom, driver: this, setDriver, setTranslateY }) 24 | } 25 | 26 | this.shown = true 27 | this.senderBottom = bottom 28 | setDriver(this) 29 | setTranslateY(this.translateY) 30 | 31 | Animated.timing(this.animation, { 32 | toValue: 0, 33 | duration: 200, 34 | useNativeDriver: true, 35 | }).start() 36 | } 37 | 38 | hide = (state: DriverState) => { 39 | const { bottom, driver, setDriver, setTranslateY } = state 40 | 41 | this.shown = false 42 | this.y = 0 43 | this.senderBottom = bottom 44 | 45 | if (driver === this) { 46 | setDriver(undefined) 47 | setTranslateY(this.translateY) 48 | Animated.timing(this.animation, { 49 | toValue: this.height, 50 | duration: 200, 51 | useNativeDriver: true, 52 | }).start() 53 | } else { 54 | this.animation.setValue(this.height) 55 | } 56 | } 57 | 58 | toggle = (state: DriverState) => { 59 | this.shown ? this.hide(state) : this.show(state) 60 | } 61 | 62 | style = { 63 | transform: [ 64 | { 65 | translateY: this.animation, 66 | }, 67 | ], 68 | } 69 | 70 | private get position() { 71 | return this.animation.interpolate({ 72 | inputRange: [0, this.height], 73 | outputRange: [this.height, 0], 74 | }) 75 | } 76 | 77 | private get translateY() { 78 | const extraHeight = this.senderBottom 79 | console.log(this.name, 'height', this.height, 'y', this.y, 'extraHeight', extraHeight) 80 | if (!this.shown || this.y === 0) { 81 | return this.position.interpolate({ 82 | inputRange: [extraHeight, this.height], 83 | outputRange: [0, extraHeight - this.height], 84 | extrapolate: 'clamp', 85 | }) as Animated.Value 86 | } else { 87 | return this.position.interpolate({ 88 | inputRange: [0, this.height], 89 | outputRange: [extraHeight - this.y, extraHeight - this.height], 90 | extrapolate: 'clamp', 91 | }) as Animated.Value 92 | } 93 | } 94 | 95 | onLayout = (event: LayoutChangeEvent) => { 96 | this.animation.setValue(event.nativeEvent.layout.height) 97 | this.height = event.nativeEvent.layout.height 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /example/KeyboardChat/icon/emoji@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/listenzz/react-native-keyboard-insets/7bc81ed8f6434afc4321b71fbf33cfcff20fbde4/example/KeyboardChat/icon/emoji@2x.png -------------------------------------------------------------------------------- /example/KeyboardChat/icon/emoji@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/listenzz/react-native-keyboard-insets/7bc81ed8f6434afc4321b71fbf33cfcff20fbde4/example/KeyboardChat/icon/emoji@3x.png -------------------------------------------------------------------------------- /example/KeyboardChat/icon/keyboard@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/listenzz/react-native-keyboard-insets/7bc81ed8f6434afc4321b71fbf33cfcff20fbde4/example/KeyboardChat/icon/keyboard@2x.png -------------------------------------------------------------------------------- /example/KeyboardChat/icon/keyboard@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/listenzz/react-native-keyboard-insets/7bc81ed8f6434afc4321b71fbf33cfcff20fbde4/example/KeyboardChat/icon/keyboard@3x.png -------------------------------------------------------------------------------- /example/KeyboardChat/icon/plus@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/listenzz/react-native-keyboard-insets/7bc81ed8f6434afc4321b71fbf33cfcff20fbde4/example/KeyboardChat/icon/plus@2x.png -------------------------------------------------------------------------------- /example/KeyboardChat/icon/plus@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/listenzz/react-native-keyboard-insets/7bc81ed8f6434afc4321b71fbf33cfcff20fbde4/example/KeyboardChat/icon/plus@3x.png -------------------------------------------------------------------------------- /example/KeyboardChat/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { useCallback, useRef, useState } from 'react' 2 | import { Animated, findNodeHandle, Image, Pressable, ScrollView, Text, TextInput, View } from 'react-native' 3 | 4 | import Message from './Message' 5 | import { history } from './Message/data' 6 | import styles from './styles' 7 | import { SafeAreaProvider, SafeAreaView } from 'react-native-safe-area-context' 8 | import { KeyboardInsetsView, getEdgeInsetsForView } from 'react-native-keyboard-insets' 9 | import { withNavigationItem } from 'hybrid-navigation' 10 | import { ViewDriver } from './driver/ViewDriver' 11 | import { Driver } from './driver/Driver' 12 | import { KeyboardDriver } from './driver/KeyboardDriver' 13 | 14 | function KeyboardChat() { 15 | const inputRef = useRef(null) 16 | const senderRef = useRef(null) 17 | const [bottom, setBottom] = useState(0) 18 | 19 | const onLayout = useCallback(() => { 20 | const viewTag = findNodeHandle(senderRef.current) 21 | if (viewTag === null) { 22 | return 23 | } 24 | 25 | getEdgeInsetsForView(viewTag, insets => { 26 | setBottom(insets.bottom!) 27 | }) 28 | }, []) 29 | 30 | const emoji = useRef(new ViewDriver('emoji')).current 31 | const toolbox = useRef(new ViewDriver('toolbox')).current 32 | const keyboard = useRef(new KeyboardDriver(inputRef)).current 33 | 34 | const [driver, setDriver] = useState() 35 | const [translateY, setTranslateY] = useState(new Animated.Value(0)) 36 | const driverState = { bottom, driver, setDriver, setTranslateY } 37 | 38 | const mainStyle = { 39 | transform: [ 40 | { 41 | translateY: translateY, 42 | }, 43 | ], 44 | } 45 | 46 | return ( 47 | 48 | 49 | 50 | 51 | {history.map((message, index) => ( 52 | 53 | ))} 54 | 55 | 56 | 57 | 58 | (emoji.shown ? keyboard.show() : emoji.show(driverState))}> 59 | 60 | 61 | toolbox.toggle(driverState)}> 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 表情包 71 | 72 | 73 | 74 | 75 | 76 | 工具箱 77 | 78 | 79 | 80 | 81 | ) 82 | } 83 | 84 | export default withNavigationItem({ 85 | titleItem: { 86 | title: '聊天键盘处理', 87 | }, 88 | })(KeyboardChat) 89 | -------------------------------------------------------------------------------- /example/KeyboardChat/styles.ts: -------------------------------------------------------------------------------- 1 | import { StyleSheet } from 'react-native' 2 | 3 | export default StyleSheet.create({ 4 | provider: { 5 | backgroundColor: '#FFFFFF', 6 | }, 7 | fill: { 8 | flex: 1, 9 | }, 10 | header: { 11 | marginRight: 12, 12 | }, 13 | inverted: { 14 | transform: [ 15 | { 16 | scaleY: -1, 17 | }, 18 | ], 19 | }, 20 | sender: { 21 | flexDirection: 'row', 22 | backgroundColor: 'white', 23 | alignItems: 'center', 24 | borderTopColor: '#DDDDDD', 25 | borderTopWidth: 1, 26 | paddingRight: 6, 27 | paddingVertical: 6, 28 | }, 29 | input: { 30 | borderWidth: 1, 31 | borderColor: '#DDDDDD', 32 | backgroundColor: '#F5F5F5', 33 | flex: 1, 34 | minHeight: 36, 35 | maxHeight: 80, 36 | fontSize: 14, 37 | paddingTop: 10, 38 | paddingBottom: 10, 39 | paddingHorizontal: 6, 40 | marginHorizontal: 6, 41 | }, 42 | button: { 43 | width: 36, 44 | height: 36, 45 | justifyContent: 'center', 46 | alignItems: 'center', 47 | }, 48 | absolute: { 49 | position: 'absolute', 50 | bottom: 0, 51 | left: 0, 52 | right: 0, 53 | }, 54 | red: { 55 | backgroundColor: 'cadetblue', 56 | }, 57 | emoji: { 58 | height: 300, 59 | justifyContent: 'center', 60 | alignItems: 'center', 61 | }, 62 | blue: { 63 | backgroundColor: 'darkkhaki', 64 | }, 65 | toolbox: { 66 | height: 200, 67 | justifyContent: 'center', 68 | alignItems: 'center', 69 | }, 70 | text: { 71 | fontSize: 48, 72 | color: 'darkgray', 73 | }, 74 | }) 75 | -------------------------------------------------------------------------------- /example/android/app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | project.ext.react = [ 3 | root : "../../../", 4 | entryFile : "example/index.js", 5 | cliPath : "../../../node_modules/react-native/cli.js", 6 | bundleInRelease: true, 7 | bundleInDebug : true, 8 | enableHermes : false, 9 | hermesCommand : "../../../node_modules/hermes-engine/%OS-BIN%/hermesc", 10 | ] 11 | 12 | apply from: "../../../node_modules/react-native/react.gradle" 13 | 14 | def enableHermes = project.ext.react.get("enableHermes", false) 15 | def enableSeparateBuildPerCPUArchitecture = false 16 | def enableProguardInReleaseBuilds = false 17 | def jscFlavor = 'org.webkit:android-jsc:+' 18 | 19 | android { 20 | compileSdkVersion rootProject.ext.compileSdkVersion 21 | 22 | compileOptions { 23 | sourceCompatibility JavaVersion.VERSION_1_8 24 | targetCompatibility JavaVersion.VERSION_1_8 25 | } 26 | 27 | defaultConfig { 28 | applicationId "com.reactnative.keyboardinsets.example" 29 | minSdkVersion rootProject.ext.minSdkVersion 30 | targetSdkVersion rootProject.ext.targetSdkVersion 31 | versionCode 1 32 | versionName "1.0" 33 | } 34 | splits { 35 | abi { 36 | reset() 37 | enable enableSeparateBuildPerCPUArchitecture 38 | universalApk false // If true, also generate a universal APK 39 | include "armeabi-v7a", "x86", "arm64-v8a", "x86_64" 40 | } 41 | } 42 | signingConfigs { 43 | debug { 44 | storeFile file('debug.keystore') 45 | storePassword 'android' 46 | keyAlias 'androiddebugkey' 47 | keyPassword 'android' 48 | } 49 | } 50 | buildTypes { 51 | debug { 52 | signingConfig signingConfigs.debug 53 | } 54 | release { 55 | // Caution! In production, you need to generate your own keystore file. 56 | // see https://facebook.github.io/react-native/docs/signed-apk-android. 57 | signingConfig signingConfigs.debug 58 | minifyEnabled enableProguardInReleaseBuilds 59 | proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" 60 | } 61 | } 62 | } 63 | 64 | dependencies { 65 | implementation fileTree(dir: "libs", include: ["*.jar"]) 66 | implementation 'com.facebook.react:react-native:+' 67 | implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0" 68 | if (enableHermes) { 69 | def hermesPath = "../../../node_modules/hermes-engine/android/" 70 | debugImplementation files(hermesPath + "hermes-debug.aar") 71 | releaseImplementation files(hermesPath + "hermes-release.aar") 72 | } else { 73 | implementation jscFlavor 74 | } 75 | implementation project(':react-native-keyboard-insets') 76 | } 77 | 78 | task copyDownloadableDepsToLibs(type: Copy) { 79 | from configurations.implementation 80 | into 'libs' 81 | } 82 | 83 | apply from: file("../../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); 84 | applyNativeModulesAppBuildGradle(project, "../..") -------------------------------------------------------------------------------- /example/android/app/debug.keystore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/listenzz/react-native-keyboard-insets/7bc81ed8f6434afc4321b71fbf33cfcff20fbde4/example/android/app/debug.keystore -------------------------------------------------------------------------------- /example/android/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | -------------------------------------------------------------------------------- /example/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /example/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 16 | 17 | 20 | 23 | 26 | 27 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /example/android/app/src/main/java/com/reactnative/keyboardinsets/example/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.reactnative.keyboardinsets.example; 2 | 3 | import com.reactnative.hybridnavigation.ReactAppCompatActivity; 4 | 5 | public class MainActivity extends ReactAppCompatActivity { 6 | 7 | } -------------------------------------------------------------------------------- /example/android/app/src/main/java/com/reactnative/keyboardinsets/example/MainApplication.java: -------------------------------------------------------------------------------- 1 | package com.reactnative.keyboardinsets.example; 2 | 3 | import android.app.Application; 4 | 5 | import com.facebook.common.logging.FLog; 6 | import com.facebook.react.PackageList; 7 | import com.facebook.react.ReactApplication; 8 | import com.facebook.react.ReactNativeHost; 9 | import com.facebook.react.ReactPackage; 10 | import com.facebook.soloader.SoLoader; 11 | import java.util.List; 12 | import com.reactnative.hybridnavigation.ReactBridgeManager; 13 | import com.reactnative.keyboardinsets.KeyboardInsetsPackage; 14 | 15 | public class MainApplication extends Application implements ReactApplication { 16 | 17 | private final ReactNativeHost mReactNativeHost = 18 | new ReactNativeHost(this) { 19 | @Override 20 | public boolean getUseDeveloperSupport() { 21 | return BuildConfig.DEBUG; 22 | } 23 | 24 | @Override 25 | protected List getPackages() { 26 | @SuppressWarnings("UnnecessaryLocalVariable") 27 | List packages = new PackageList(this).getPackages(); 28 | packages.add(new KeyboardInsetsPackage()); 29 | return packages; 30 | } 31 | 32 | @Override 33 | protected String getJSMainModuleName() { 34 | return "example/index"; 35 | } 36 | }; 37 | 38 | @Override 39 | public ReactNativeHost getReactNativeHost() { 40 | return mReactNativeHost; 41 | } 42 | 43 | @Override 44 | public void onCreate() { 45 | super.onCreate(); 46 | SoLoader.init(this, /* native exopackage */ false); 47 | ReactBridgeManager bridgeManager = ReactBridgeManager.get(); 48 | bridgeManager.install(getReactNativeHost()); 49 | FLog.setMinimumLoggingLevel(FLog.INFO); 50 | } 51 | } -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/listenzz/react-native-keyboard-insets/7bc81ed8f6434afc4321b71fbf33cfcff20fbde4/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/listenzz/react-native-keyboard-insets/7bc81ed8f6434afc4321b71fbf33cfcff20fbde4/example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/listenzz/react-native-keyboard-insets/7bc81ed8f6434afc4321b71fbf33cfcff20fbde4/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/listenzz/react-native-keyboard-insets/7bc81ed8f6434afc4321b71fbf33cfcff20fbde4/example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/listenzz/react-native-keyboard-insets/7bc81ed8f6434afc4321b71fbf33cfcff20fbde4/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/listenzz/react-native-keyboard-insets/7bc81ed8f6434afc4321b71fbf33cfcff20fbde4/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/listenzz/react-native-keyboard-insets/7bc81ed8f6434afc4321b71fbf33cfcff20fbde4/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/listenzz/react-native-keyboard-insets/7bc81ed8f6434afc4321b71fbf33cfcff20fbde4/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/listenzz/react-native-keyboard-insets/7bc81ed8f6434afc4321b71fbf33cfcff20fbde4/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/listenzz/react-native-keyboard-insets/7bc81ed8f6434afc4321b71fbf33cfcff20fbde4/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | KeyboardInsetsExample 3 | 4 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /example/android/build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | ext { 5 | buildToolsVersion = "30.0.2" 6 | minSdkVersion = 21 7 | compileSdkVersion = 30 8 | targetSdkVersion = 30 9 | } 10 | repositories { 11 | google() 12 | mavenCentral() 13 | } 14 | dependencies { 15 | classpath("com.android.tools.build:gradle:7.3.1") 16 | // NOTE: Do not place your application dependencies here; they belong 17 | // in the individual module build.gradle files 18 | } 19 | } 20 | 21 | allprojects { 22 | repositories { 23 | maven { 24 | // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm 25 | url("$rootDir/../../node_modules/react-native/android") 26 | } 27 | maven { 28 | // Android JSC is installed from npm 29 | url("$rootDir/../../node_modules/jsc-android/dist") 30 | } 31 | mavenCentral { 32 | // We don't want to fetch react-native from Maven Central as there are 33 | // older versions over there. 34 | content { 35 | excludeGroup "com.facebook.react" 36 | } 37 | } 38 | google() 39 | maven { url 'https://www.jitpack.io' } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /example/android/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx1024m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true 19 | 20 | android.useAndroidX=true 21 | android.enableJetifier=true 22 | -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/listenzz/react-native-keyboard-insets/7bc81ed8f6434afc4321b71fbf33cfcff20fbde4/example/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | zipStoreBase=GRADLE_USER_HOME 4 | zipStorePath=wrapper/dists 5 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-all.zip -------------------------------------------------------------------------------- /example/android/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /example/android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /example/android/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'KeyboardInsetsExample' 2 | apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle") 3 | applyNativeModulesSettingsGradle(settings, "../..") 4 | 5 | include ':app' 6 | 7 | include ':react-native-keyboard-insets' 8 | project(':react-native-keyboard-insets').projectDir = new File(rootProject.projectDir, '../../android') -------------------------------------------------------------------------------- /example/index.js: -------------------------------------------------------------------------------- 1 | import { Platform } from 'react-native' 2 | import Navigation, { BarStyleDarkContent } from 'hybrid-navigation' 3 | import App from './App' 4 | import KeyboardChat from './KeyboardChat' 5 | 6 | Navigation.setDefaultOptions({ 7 | screenBackgroundColor: '#F8F8F8', 8 | topBarStyle: BarStyleDarkContent, 9 | statusBarColorAndroid: Platform.Version > 21 ? undefined : '#4A4A4A', 10 | navigationBarColorAndroid: '#FFFFFF', 11 | }) 12 | 13 | Navigation.startRegisterComponent() 14 | Navigation.registerComponent('KeyboardInsets', () => App) 15 | Navigation.registerComponent('Chat', () => KeyboardChat) 16 | Navigation.endRegisterComponent() 17 | 18 | Navigation.setRoot({ 19 | stack: { 20 | children: [ 21 | { 22 | screen: { 23 | moduleName: 'KeyboardInsets', 24 | }, 25 | }, 26 | ], 27 | }, 28 | }) 29 | -------------------------------------------------------------------------------- /example/ios/KeyboardInsetsExample.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 55; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 047B8517EA7B5047F0A2DD5B /* libPods-KeyboardInsetsExample.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E122DC6660A2FD7515F28ED7 /* libPods-KeyboardInsetsExample.a */; }; 11 | 13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; }; 12 | 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; 13 | 91459E512605ACD000EFBE6E /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 91459E502605ACD000EFBE6E /* Assets.xcassets */; }; 14 | 91459E552605ACE600EFBE6E /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 91459E532605ACE600EFBE6E /* LaunchScreen.storyboard */; }; 15 | /* End PBXBuildFile section */ 16 | 17 | /* Begin PBXFileReference section */ 18 | 075B3E117F0418BAAAC1F540 /* Pods-KeyboardInsetsExample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-KeyboardInsetsExample.release.xcconfig"; path = "Target Support Files/Pods-KeyboardInsetsExample/Pods-KeyboardInsetsExample.release.xcconfig"; sourceTree = ""; }; 19 | 13B07F961A680F5B00A75B9A /* KeyboardInsetsExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = KeyboardInsetsExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 20 | 13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = KeyboardInsetsExample/AppDelegate.h; sourceTree = ""; }; 21 | 13B07FB01A68108700A75B9A /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = KeyboardInsetsExample/AppDelegate.m; sourceTree = ""; }; 22 | 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = KeyboardInsetsExample/Info.plist; sourceTree = ""; }; 23 | 13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = KeyboardInsetsExample/main.m; sourceTree = ""; }; 24 | 514699031AF404FCBE8E8782 /* Pods-KeyboardInsetsExample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-KeyboardInsetsExample.debug.xcconfig"; path = "Target Support Files/Pods-KeyboardInsetsExample/Pods-KeyboardInsetsExample.debug.xcconfig"; sourceTree = ""; }; 25 | 91459E502605ACD000EFBE6E /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = KeyboardInsetsExample/Assets.xcassets; sourceTree = ""; }; 26 | 91459E542605ACE600EFBE6E /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = KeyboardInsetsExample/Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 27 | E122DC6660A2FD7515F28ED7 /* libPods-KeyboardInsetsExample.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-KeyboardInsetsExample.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 28 | ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; 29 | /* End PBXFileReference section */ 30 | 31 | /* Begin PBXFrameworksBuildPhase section */ 32 | 13B07F8C1A680F5B00A75B9A /* Frameworks */ = { 33 | isa = PBXFrameworksBuildPhase; 34 | buildActionMask = 2147483647; 35 | files = ( 36 | 047B8517EA7B5047F0A2DD5B /* libPods-KeyboardInsetsExample.a in Frameworks */, 37 | ); 38 | runOnlyForDeploymentPostprocessing = 0; 39 | }; 40 | /* End PBXFrameworksBuildPhase section */ 41 | 42 | /* Begin PBXGroup section */ 43 | 13B07FAE1A68108700A75B9A /* KeyboardInsetsExample */ = { 44 | isa = PBXGroup; 45 | children = ( 46 | 91459E532605ACE600EFBE6E /* LaunchScreen.storyboard */, 47 | 91459E502605ACD000EFBE6E /* Assets.xcassets */, 48 | 13B07FAF1A68108700A75B9A /* AppDelegate.h */, 49 | 13B07FB01A68108700A75B9A /* AppDelegate.m */, 50 | 13B07FB61A68108700A75B9A /* Info.plist */, 51 | 13B07FB71A68108700A75B9A /* main.m */, 52 | ); 53 | name = KeyboardInsetsExample; 54 | sourceTree = ""; 55 | }; 56 | 244BF2D3CD7C5A603856DFAB /* Pods */ = { 57 | isa = PBXGroup; 58 | children = ( 59 | 514699031AF404FCBE8E8782 /* Pods-KeyboardInsetsExample.debug.xcconfig */, 60 | 075B3E117F0418BAAAC1F540 /* Pods-KeyboardInsetsExample.release.xcconfig */, 61 | ); 62 | path = Pods; 63 | sourceTree = ""; 64 | }; 65 | 2D16E6871FA4F8E400B85C8A /* Frameworks */ = { 66 | isa = PBXGroup; 67 | children = ( 68 | ED297162215061F000B7C4FE /* JavaScriptCore.framework */, 69 | E122DC6660A2FD7515F28ED7 /* libPods-KeyboardInsetsExample.a */, 70 | ); 71 | name = Frameworks; 72 | sourceTree = ""; 73 | }; 74 | 832341AE1AAA6A7D00B99B32 /* Libraries */ = { 75 | isa = PBXGroup; 76 | children = ( 77 | ); 78 | name = Libraries; 79 | sourceTree = ""; 80 | }; 81 | 83CBB9F61A601CBA00E9B192 = { 82 | isa = PBXGroup; 83 | children = ( 84 | 13B07FAE1A68108700A75B9A /* KeyboardInsetsExample */, 85 | 832341AE1AAA6A7D00B99B32 /* Libraries */, 86 | 83CBBA001A601CBA00E9B192 /* Products */, 87 | 2D16E6871FA4F8E400B85C8A /* Frameworks */, 88 | 244BF2D3CD7C5A603856DFAB /* Pods */, 89 | ); 90 | indentWidth = 4; 91 | sourceTree = ""; 92 | tabWidth = 4; 93 | usesTabs = 0; 94 | }; 95 | 83CBBA001A601CBA00E9B192 /* Products */ = { 96 | isa = PBXGroup; 97 | children = ( 98 | 13B07F961A680F5B00A75B9A /* KeyboardInsetsExample.app */, 99 | ); 100 | name = Products; 101 | sourceTree = ""; 102 | }; 103 | /* End PBXGroup section */ 104 | 105 | /* Begin PBXNativeTarget section */ 106 | 13B07F861A680F5B00A75B9A /* KeyboardInsetsExample */ = { 107 | isa = PBXNativeTarget; 108 | buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "KeyboardInsetsExample" */; 109 | buildPhases = ( 110 | 0B094BC46D5831EE808180B5 /* [CP] Check Pods Manifest.lock */, 111 | 13B07F871A680F5B00A75B9A /* Sources */, 112 | 13B07F8C1A680F5B00A75B9A /* Frameworks */, 113 | 13B07F8E1A680F5B00A75B9A /* Resources */, 114 | 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */, 115 | 1D167FDFE527CD5394527AC6 /* [CP] Copy Pods Resources */, 116 | ); 117 | buildRules = ( 118 | ); 119 | dependencies = ( 120 | ); 121 | name = KeyboardInsetsExample; 122 | productName = KeyboardInsetsExample; 123 | productReference = 13B07F961A680F5B00A75B9A /* KeyboardInsetsExample.app */; 124 | productType = "com.apple.product-type.application"; 125 | }; 126 | /* End PBXNativeTarget section */ 127 | 128 | /* Begin PBXProject section */ 129 | 83CBB9F71A601CBA00E9B192 /* Project object */ = { 130 | isa = PBXProject; 131 | attributes = { 132 | LastUpgradeCheck = 1320; 133 | TargetAttributes = { 134 | 13B07F861A680F5B00A75B9A = { 135 | LastSwiftMigration = 1120; 136 | }; 137 | }; 138 | }; 139 | buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "KeyboardInsetsExample" */; 140 | compatibilityVersion = "Xcode 13.0"; 141 | developmentRegion = en; 142 | hasScannedForEncodings = 0; 143 | knownRegions = ( 144 | en, 145 | Base, 146 | ); 147 | mainGroup = 83CBB9F61A601CBA00E9B192; 148 | productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */; 149 | projectDirPath = ""; 150 | projectRoot = ""; 151 | targets = ( 152 | 13B07F861A680F5B00A75B9A /* KeyboardInsetsExample */, 153 | ); 154 | }; 155 | /* End PBXProject section */ 156 | 157 | /* Begin PBXResourcesBuildPhase section */ 158 | 13B07F8E1A680F5B00A75B9A /* Resources */ = { 159 | isa = PBXResourcesBuildPhase; 160 | buildActionMask = 2147483647; 161 | files = ( 162 | 91459E512605ACD000EFBE6E /* Assets.xcassets in Resources */, 163 | 91459E552605ACE600EFBE6E /* LaunchScreen.storyboard in Resources */, 164 | ); 165 | runOnlyForDeploymentPostprocessing = 0; 166 | }; 167 | /* End PBXResourcesBuildPhase section */ 168 | 169 | /* Begin PBXShellScriptBuildPhase section */ 170 | 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */ = { 171 | isa = PBXShellScriptBuildPhase; 172 | buildActionMask = 2147483647; 173 | files = ( 174 | ); 175 | inputPaths = ( 176 | ); 177 | name = "Bundle React Native code and images"; 178 | outputPaths = ( 179 | ); 180 | runOnlyForDeploymentPostprocessing = 0; 181 | shellPath = /bin/sh; 182 | shellScript = "set -e\n\nexport NODE_BINARY=node\n../../node_modules/react-native/scripts/react-native-xcode.sh example/index.js\n"; 183 | }; 184 | 0B094BC46D5831EE808180B5 /* [CP] Check Pods Manifest.lock */ = { 185 | isa = PBXShellScriptBuildPhase; 186 | buildActionMask = 2147483647; 187 | files = ( 188 | ); 189 | inputFileListPaths = ( 190 | ); 191 | inputPaths = ( 192 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 193 | "${PODS_ROOT}/Manifest.lock", 194 | ); 195 | name = "[CP] Check Pods Manifest.lock"; 196 | outputFileListPaths = ( 197 | ); 198 | outputPaths = ( 199 | "$(DERIVED_FILE_DIR)/Pods-KeyboardInsetsExample-checkManifestLockResult.txt", 200 | ); 201 | runOnlyForDeploymentPostprocessing = 0; 202 | shellPath = /bin/sh; 203 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 204 | showEnvVarsInLog = 0; 205 | }; 206 | 1D167FDFE527CD5394527AC6 /* [CP] Copy Pods Resources */ = { 207 | isa = PBXShellScriptBuildPhase; 208 | buildActionMask = 2147483647; 209 | files = ( 210 | ); 211 | inputFileListPaths = ( 212 | "${PODS_ROOT}/Target Support Files/Pods-KeyboardInsetsExample/Pods-KeyboardInsetsExample-resources-${CONFIGURATION}-input-files.xcfilelist", 213 | ); 214 | name = "[CP] Copy Pods Resources"; 215 | outputFileListPaths = ( 216 | "${PODS_ROOT}/Target Support Files/Pods-KeyboardInsetsExample/Pods-KeyboardInsetsExample-resources-${CONFIGURATION}-output-files.xcfilelist", 217 | ); 218 | runOnlyForDeploymentPostprocessing = 0; 219 | shellPath = /bin/sh; 220 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-KeyboardInsetsExample/Pods-KeyboardInsetsExample-resources.sh\"\n"; 221 | showEnvVarsInLog = 0; 222 | }; 223 | /* End PBXShellScriptBuildPhase section */ 224 | 225 | /* Begin PBXSourcesBuildPhase section */ 226 | 13B07F871A680F5B00A75B9A /* Sources */ = { 227 | isa = PBXSourcesBuildPhase; 228 | buildActionMask = 2147483647; 229 | files = ( 230 | 13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */, 231 | 13B07FC11A68108700A75B9A /* main.m in Sources */, 232 | ); 233 | runOnlyForDeploymentPostprocessing = 0; 234 | }; 235 | /* End PBXSourcesBuildPhase section */ 236 | 237 | /* Begin PBXVariantGroup section */ 238 | 91459E532605ACE600EFBE6E /* LaunchScreen.storyboard */ = { 239 | isa = PBXVariantGroup; 240 | children = ( 241 | 91459E542605ACE600EFBE6E /* Base */, 242 | ); 243 | name = LaunchScreen.storyboard; 244 | sourceTree = ""; 245 | }; 246 | /* End PBXVariantGroup section */ 247 | 248 | /* Begin XCBuildConfiguration section */ 249 | 13B07F941A680F5B00A75B9A /* Debug */ = { 250 | isa = XCBuildConfiguration; 251 | baseConfigurationReference = 514699031AF404FCBE8E8782 /* Pods-KeyboardInsetsExample.debug.xcconfig */; 252 | buildSettings = { 253 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 254 | CLANG_ENABLE_MODULES = YES; 255 | CURRENT_PROJECT_VERSION = 1; 256 | ENABLE_BITCODE = NO; 257 | INFOPLIST_FILE = KeyboardInsetsExample/Info.plist; 258 | LD_RUNPATH_SEARCH_PATHS = ( 259 | "$(inherited)", 260 | "@executable_path/Frameworks", 261 | ); 262 | OTHER_LDFLAGS = ( 263 | "$(inherited)", 264 | "-ObjC", 265 | "-lc++", 266 | ); 267 | PRODUCT_BUNDLE_IDENTIFIER = com.reactnative.keyboardinsets.example; 268 | PRODUCT_NAME = KeyboardInsetsExample; 269 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 270 | SWIFT_VERSION = 5.0; 271 | VERSIONING_SYSTEM = "apple-generic"; 272 | }; 273 | name = Debug; 274 | }; 275 | 13B07F951A680F5B00A75B9A /* Release */ = { 276 | isa = XCBuildConfiguration; 277 | baseConfigurationReference = 075B3E117F0418BAAAC1F540 /* Pods-KeyboardInsetsExample.release.xcconfig */; 278 | buildSettings = { 279 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 280 | CLANG_ENABLE_MODULES = YES; 281 | CURRENT_PROJECT_VERSION = 1; 282 | INFOPLIST_FILE = KeyboardInsetsExample/Info.plist; 283 | LD_RUNPATH_SEARCH_PATHS = ( 284 | "$(inherited)", 285 | "@executable_path/Frameworks", 286 | ); 287 | OTHER_LDFLAGS = ( 288 | "$(inherited)", 289 | "-ObjC", 290 | "-lc++", 291 | ); 292 | PRODUCT_BUNDLE_IDENTIFIER = com.reactnative.keyboardinsets.example; 293 | PRODUCT_NAME = KeyboardInsetsExample; 294 | SWIFT_VERSION = 5.0; 295 | VERSIONING_SYSTEM = "apple-generic"; 296 | }; 297 | name = Release; 298 | }; 299 | 83CBBA201A601CBA00E9B192 /* Debug */ = { 300 | isa = XCBuildConfiguration; 301 | buildSettings = { 302 | ALWAYS_SEARCH_USER_PATHS = NO; 303 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 304 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 305 | CLANG_CXX_LIBRARY = "libc++"; 306 | CLANG_ENABLE_MODULES = YES; 307 | CLANG_ENABLE_OBJC_ARC = YES; 308 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 309 | CLANG_WARN_BOOL_CONVERSION = YES; 310 | CLANG_WARN_COMMA = YES; 311 | CLANG_WARN_CONSTANT_CONVERSION = YES; 312 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 313 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 314 | CLANG_WARN_EMPTY_BODY = YES; 315 | CLANG_WARN_ENUM_CONVERSION = YES; 316 | CLANG_WARN_INFINITE_RECURSION = YES; 317 | CLANG_WARN_INT_CONVERSION = YES; 318 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 319 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 320 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 321 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 322 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 323 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 324 | CLANG_WARN_STRICT_PROTOTYPES = YES; 325 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 326 | CLANG_WARN_UNREACHABLE_CODE = YES; 327 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 328 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 329 | COPY_PHASE_STRIP = NO; 330 | ENABLE_STRICT_OBJC_MSGSEND = YES; 331 | ENABLE_TESTABILITY = YES; 332 | "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = ""; 333 | GCC_C_LANGUAGE_STANDARD = gnu99; 334 | GCC_DYNAMIC_NO_PIC = NO; 335 | GCC_NO_COMMON_BLOCKS = YES; 336 | GCC_OPTIMIZATION_LEVEL = 0; 337 | GCC_PREPROCESSOR_DEFINITIONS = ( 338 | "DEBUG=1", 339 | "$(inherited)", 340 | ); 341 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 342 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 343 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 344 | GCC_WARN_UNDECLARED_SELECTOR = YES; 345 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 346 | GCC_WARN_UNUSED_FUNCTION = YES; 347 | GCC_WARN_UNUSED_VARIABLE = YES; 348 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 349 | LD_RUNPATH_SEARCH_PATHS = ( 350 | /usr/lib/swift, 351 | "$(inherited)", 352 | ); 353 | LIBRARY_SEARCH_PATHS = ( 354 | "$(SDKROOT)/usr/lib/swift", 355 | "\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"", 356 | "\"$(inherited)\"", 357 | ); 358 | MTL_ENABLE_DEBUG_INFO = YES; 359 | ONLY_ACTIVE_ARCH = YES; 360 | SDKROOT = iphoneos; 361 | }; 362 | name = Debug; 363 | }; 364 | 83CBBA211A601CBA00E9B192 /* Release */ = { 365 | isa = XCBuildConfiguration; 366 | buildSettings = { 367 | ALWAYS_SEARCH_USER_PATHS = NO; 368 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 369 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 370 | CLANG_CXX_LIBRARY = "libc++"; 371 | CLANG_ENABLE_MODULES = YES; 372 | CLANG_ENABLE_OBJC_ARC = YES; 373 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 374 | CLANG_WARN_BOOL_CONVERSION = YES; 375 | CLANG_WARN_COMMA = YES; 376 | CLANG_WARN_CONSTANT_CONVERSION = YES; 377 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 378 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 379 | CLANG_WARN_EMPTY_BODY = YES; 380 | CLANG_WARN_ENUM_CONVERSION = YES; 381 | CLANG_WARN_INFINITE_RECURSION = YES; 382 | CLANG_WARN_INT_CONVERSION = YES; 383 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 384 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 385 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 386 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 387 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 388 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 389 | CLANG_WARN_STRICT_PROTOTYPES = YES; 390 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 391 | CLANG_WARN_UNREACHABLE_CODE = YES; 392 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 393 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 394 | COPY_PHASE_STRIP = YES; 395 | ENABLE_NS_ASSERTIONS = NO; 396 | ENABLE_STRICT_OBJC_MSGSEND = YES; 397 | "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = ""; 398 | GCC_C_LANGUAGE_STANDARD = gnu99; 399 | GCC_NO_COMMON_BLOCKS = YES; 400 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 401 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 402 | GCC_WARN_UNDECLARED_SELECTOR = YES; 403 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 404 | GCC_WARN_UNUSED_FUNCTION = YES; 405 | GCC_WARN_UNUSED_VARIABLE = YES; 406 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 407 | LD_RUNPATH_SEARCH_PATHS = ( 408 | /usr/lib/swift, 409 | "$(inherited)", 410 | ); 411 | LIBRARY_SEARCH_PATHS = ( 412 | "$(SDKROOT)/usr/lib/swift", 413 | "\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"", 414 | "\"$(inherited)\"", 415 | ); 416 | MTL_ENABLE_DEBUG_INFO = NO; 417 | SDKROOT = iphoneos; 418 | VALIDATE_PRODUCT = YES; 419 | }; 420 | name = Release; 421 | }; 422 | /* End XCBuildConfiguration section */ 423 | 424 | /* Begin XCConfigurationList section */ 425 | 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "KeyboardInsetsExample" */ = { 426 | isa = XCConfigurationList; 427 | buildConfigurations = ( 428 | 13B07F941A680F5B00A75B9A /* Debug */, 429 | 13B07F951A680F5B00A75B9A /* Release */, 430 | ); 431 | defaultConfigurationIsVisible = 0; 432 | defaultConfigurationName = Release; 433 | }; 434 | 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "KeyboardInsetsExample" */ = { 435 | isa = XCConfigurationList; 436 | buildConfigurations = ( 437 | 83CBBA201A601CBA00E9B192 /* Debug */, 438 | 83CBBA211A601CBA00E9B192 /* Release */, 439 | ); 440 | defaultConfigurationIsVisible = 0; 441 | defaultConfigurationName = Release; 442 | }; 443 | /* End XCConfigurationList section */ 444 | }; 445 | rootObject = 83CBB9F71A601CBA00E9B192 /* Project object */; 446 | } 447 | -------------------------------------------------------------------------------- /example/ios/KeyboardInsetsExample.xcodeproj/xcshareddata/xcschemes/KeyboardInsetsExample.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 45 | 51 | 52 | 53 | 54 | 60 | 62 | 68 | 69 | 70 | 71 | 73 | 74 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /example/ios/KeyboardInsetsExample.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /example/ios/KeyboardInsetsExample/AppDelegate.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface AppDelegate : UIResponder 4 | 5 | @property (strong, nonatomic) UIWindow *window; 6 | 7 | @end -------------------------------------------------------------------------------- /example/ios/KeyboardInsetsExample/AppDelegate.m: -------------------------------------------------------------------------------- 1 | #import "AppDelegate.h" 2 | #import 3 | #import 4 | #import 5 | 6 | @interface AppDelegate () 7 | 8 | @end 9 | 10 | @implementation AppDelegate 11 | 12 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 13 | 14 | RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions]; 15 | [[HBDReactBridgeManager get] installWithBridge:bridge]; 16 | 17 | UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"LaunchScreen" bundle:nil]; 18 | UIViewController *rootViewController = [storyboard instantiateInitialViewController]; 19 | self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; 20 | self.window.windowLevel = UIWindowLevelStatusBar + 1; 21 | self.window.rootViewController = rootViewController; 22 | [self.window makeKeyAndVisible]; 23 | return YES; 24 | } 25 | 26 | - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge { 27 | #if DEBUG 28 | return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"example/index" fallbackResource:nil]; 29 | #else 30 | return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; 31 | #endif 32 | } 33 | 34 | @end 35 | -------------------------------------------------------------------------------- /example/ios/KeyboardInsetsExample/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /example/ios/KeyboardInsetsExample/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /example/ios/KeyboardInsetsExample/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /example/ios/KeyboardInsetsExample/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | $(PRODUCT_NAME) 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | LSRequiresIPhoneOS 26 | 27 | NSAppTransportSecurity 28 | 29 | NSAllowsArbitraryLoads 30 | 31 | NSExceptionDomains 32 | 33 | localhost 34 | 35 | NSExceptionAllowsInsecureHTTPLoads 36 | 37 | 38 | 39 | 40 | UILaunchStoryboardName 41 | LaunchScreen 42 | UIRequiredDeviceCapabilities 43 | 44 | armv7 45 | 46 | UIStatusBarHidden 47 | 48 | UISupportedInterfaceOrientations 49 | 50 | UIInterfaceOrientationPortrait 51 | 52 | 53 | -------------------------------------------------------------------------------- /example/ios/KeyboardInsetsExample/main.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import "AppDelegate.h" 3 | 4 | int main(int argc, char * argv[]) { 5 | NSString * appDelegateClassName; 6 | @autoreleasepool { 7 | // Setup code that might create autoreleased objects goes here. 8 | appDelegateClassName = NSStringFromClass([AppDelegate class]); 9 | } 10 | return UIApplicationMain(argc, argv, nil, appDelegateClassName); 11 | } -------------------------------------------------------------------------------- /example/ios/Podfile: -------------------------------------------------------------------------------- 1 | platform :ios, '11.0' 2 | require_relative '../../node_modules/react-native/scripts/react_native_pods' 3 | require_relative '../../node_modules/@react-native-community/cli-platform-ios/native_modules' 4 | 5 | target 'KeyboardInsetsExample' do 6 | pod 'RNKeyboardInsets', :path => '../../' 7 | 8 | config = use_native_modules! 9 | use_react_native!( 10 | :path => config[:reactNativePath], 11 | :hermes_enabled => false 12 | ) 13 | end 14 | 15 | post_install do |installer| 16 | react_native_post_install(installer) 17 | __apply_Xcode_12_5_M1_post_install_workaround(installer) 18 | end 19 | -------------------------------------------------------------------------------- /example/ios/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - boost (1.76.0) 3 | - DoubleConversion (1.1.6) 4 | - FBLazyVector (0.67.4) 5 | - FBReactNativeSpec (0.67.4): 6 | - RCT-Folly (= 2021.06.28.00-v2) 7 | - RCTRequired (= 0.67.4) 8 | - RCTTypeSafety (= 0.67.4) 9 | - React-Core (= 0.67.4) 10 | - React-jsi (= 0.67.4) 11 | - ReactCommon/turbomodule/core (= 0.67.4) 12 | - fmt (6.2.1) 13 | - glog (0.3.5) 14 | - HybridNavigation (2.14.6): 15 | - React-Core 16 | - RCT-Folly (2021.06.28.00-v2): 17 | - boost 18 | - DoubleConversion 19 | - fmt (~> 6.2.1) 20 | - glog 21 | - RCT-Folly/Default (= 2021.06.28.00-v2) 22 | - RCT-Folly/Default (2021.06.28.00-v2): 23 | - boost 24 | - DoubleConversion 25 | - fmt (~> 6.2.1) 26 | - glog 27 | - RCTRequired (0.67.4) 28 | - RCTTypeSafety (0.67.4): 29 | - FBLazyVector (= 0.67.4) 30 | - RCT-Folly (= 2021.06.28.00-v2) 31 | - RCTRequired (= 0.67.4) 32 | - React-Core (= 0.67.4) 33 | - React (0.67.4): 34 | - React-Core (= 0.67.4) 35 | - React-Core/DevSupport (= 0.67.4) 36 | - React-Core/RCTWebSocket (= 0.67.4) 37 | - React-RCTActionSheet (= 0.67.4) 38 | - React-RCTAnimation (= 0.67.4) 39 | - React-RCTBlob (= 0.67.4) 40 | - React-RCTImage (= 0.67.4) 41 | - React-RCTLinking (= 0.67.4) 42 | - React-RCTNetwork (= 0.67.4) 43 | - React-RCTSettings (= 0.67.4) 44 | - React-RCTText (= 0.67.4) 45 | - React-RCTVibration (= 0.67.4) 46 | - React-callinvoker (0.67.4) 47 | - React-Core (0.67.4): 48 | - glog 49 | - RCT-Folly (= 2021.06.28.00-v2) 50 | - React-Core/Default (= 0.67.4) 51 | - React-cxxreact (= 0.67.4) 52 | - React-jsi (= 0.67.4) 53 | - React-jsiexecutor (= 0.67.4) 54 | - React-perflogger (= 0.67.4) 55 | - Yoga 56 | - React-Core/CoreModulesHeaders (0.67.4): 57 | - glog 58 | - RCT-Folly (= 2021.06.28.00-v2) 59 | - React-Core/Default 60 | - React-cxxreact (= 0.67.4) 61 | - React-jsi (= 0.67.4) 62 | - React-jsiexecutor (= 0.67.4) 63 | - React-perflogger (= 0.67.4) 64 | - Yoga 65 | - React-Core/Default (0.67.4): 66 | - glog 67 | - RCT-Folly (= 2021.06.28.00-v2) 68 | - React-cxxreact (= 0.67.4) 69 | - React-jsi (= 0.67.4) 70 | - React-jsiexecutor (= 0.67.4) 71 | - React-perflogger (= 0.67.4) 72 | - Yoga 73 | - React-Core/DevSupport (0.67.4): 74 | - glog 75 | - RCT-Folly (= 2021.06.28.00-v2) 76 | - React-Core/Default (= 0.67.4) 77 | - React-Core/RCTWebSocket (= 0.67.4) 78 | - React-cxxreact (= 0.67.4) 79 | - React-jsi (= 0.67.4) 80 | - React-jsiexecutor (= 0.67.4) 81 | - React-jsinspector (= 0.67.4) 82 | - React-perflogger (= 0.67.4) 83 | - Yoga 84 | - React-Core/RCTActionSheetHeaders (0.67.4): 85 | - glog 86 | - RCT-Folly (= 2021.06.28.00-v2) 87 | - React-Core/Default 88 | - React-cxxreact (= 0.67.4) 89 | - React-jsi (= 0.67.4) 90 | - React-jsiexecutor (= 0.67.4) 91 | - React-perflogger (= 0.67.4) 92 | - Yoga 93 | - React-Core/RCTAnimationHeaders (0.67.4): 94 | - glog 95 | - RCT-Folly (= 2021.06.28.00-v2) 96 | - React-Core/Default 97 | - React-cxxreact (= 0.67.4) 98 | - React-jsi (= 0.67.4) 99 | - React-jsiexecutor (= 0.67.4) 100 | - React-perflogger (= 0.67.4) 101 | - Yoga 102 | - React-Core/RCTBlobHeaders (0.67.4): 103 | - glog 104 | - RCT-Folly (= 2021.06.28.00-v2) 105 | - React-Core/Default 106 | - React-cxxreact (= 0.67.4) 107 | - React-jsi (= 0.67.4) 108 | - React-jsiexecutor (= 0.67.4) 109 | - React-perflogger (= 0.67.4) 110 | - Yoga 111 | - React-Core/RCTImageHeaders (0.67.4): 112 | - glog 113 | - RCT-Folly (= 2021.06.28.00-v2) 114 | - React-Core/Default 115 | - React-cxxreact (= 0.67.4) 116 | - React-jsi (= 0.67.4) 117 | - React-jsiexecutor (= 0.67.4) 118 | - React-perflogger (= 0.67.4) 119 | - Yoga 120 | - React-Core/RCTLinkingHeaders (0.67.4): 121 | - glog 122 | - RCT-Folly (= 2021.06.28.00-v2) 123 | - React-Core/Default 124 | - React-cxxreact (= 0.67.4) 125 | - React-jsi (= 0.67.4) 126 | - React-jsiexecutor (= 0.67.4) 127 | - React-perflogger (= 0.67.4) 128 | - Yoga 129 | - React-Core/RCTNetworkHeaders (0.67.4): 130 | - glog 131 | - RCT-Folly (= 2021.06.28.00-v2) 132 | - React-Core/Default 133 | - React-cxxreact (= 0.67.4) 134 | - React-jsi (= 0.67.4) 135 | - React-jsiexecutor (= 0.67.4) 136 | - React-perflogger (= 0.67.4) 137 | - Yoga 138 | - React-Core/RCTSettingsHeaders (0.67.4): 139 | - glog 140 | - RCT-Folly (= 2021.06.28.00-v2) 141 | - React-Core/Default 142 | - React-cxxreact (= 0.67.4) 143 | - React-jsi (= 0.67.4) 144 | - React-jsiexecutor (= 0.67.4) 145 | - React-perflogger (= 0.67.4) 146 | - Yoga 147 | - React-Core/RCTTextHeaders (0.67.4): 148 | - glog 149 | - RCT-Folly (= 2021.06.28.00-v2) 150 | - React-Core/Default 151 | - React-cxxreact (= 0.67.4) 152 | - React-jsi (= 0.67.4) 153 | - React-jsiexecutor (= 0.67.4) 154 | - React-perflogger (= 0.67.4) 155 | - Yoga 156 | - React-Core/RCTVibrationHeaders (0.67.4): 157 | - glog 158 | - RCT-Folly (= 2021.06.28.00-v2) 159 | - React-Core/Default 160 | - React-cxxreact (= 0.67.4) 161 | - React-jsi (= 0.67.4) 162 | - React-jsiexecutor (= 0.67.4) 163 | - React-perflogger (= 0.67.4) 164 | - Yoga 165 | - React-Core/RCTWebSocket (0.67.4): 166 | - glog 167 | - RCT-Folly (= 2021.06.28.00-v2) 168 | - React-Core/Default (= 0.67.4) 169 | - React-cxxreact (= 0.67.4) 170 | - React-jsi (= 0.67.4) 171 | - React-jsiexecutor (= 0.67.4) 172 | - React-perflogger (= 0.67.4) 173 | - Yoga 174 | - React-CoreModules (0.67.4): 175 | - FBReactNativeSpec (= 0.67.4) 176 | - RCT-Folly (= 2021.06.28.00-v2) 177 | - RCTTypeSafety (= 0.67.4) 178 | - React-Core/CoreModulesHeaders (= 0.67.4) 179 | - React-jsi (= 0.67.4) 180 | - React-RCTImage (= 0.67.4) 181 | - ReactCommon/turbomodule/core (= 0.67.4) 182 | - React-cxxreact (0.67.4): 183 | - boost (= 1.76.0) 184 | - DoubleConversion 185 | - glog 186 | - RCT-Folly (= 2021.06.28.00-v2) 187 | - React-callinvoker (= 0.67.4) 188 | - React-jsi (= 0.67.4) 189 | - React-jsinspector (= 0.67.4) 190 | - React-logger (= 0.67.4) 191 | - React-perflogger (= 0.67.4) 192 | - React-runtimeexecutor (= 0.67.4) 193 | - React-jsi (0.67.4): 194 | - boost (= 1.76.0) 195 | - DoubleConversion 196 | - glog 197 | - RCT-Folly (= 2021.06.28.00-v2) 198 | - React-jsi/Default (= 0.67.4) 199 | - React-jsi/Default (0.67.4): 200 | - boost (= 1.76.0) 201 | - DoubleConversion 202 | - glog 203 | - RCT-Folly (= 2021.06.28.00-v2) 204 | - React-jsiexecutor (0.67.4): 205 | - DoubleConversion 206 | - glog 207 | - RCT-Folly (= 2021.06.28.00-v2) 208 | - React-cxxreact (= 0.67.4) 209 | - React-jsi (= 0.67.4) 210 | - React-perflogger (= 0.67.4) 211 | - React-jsinspector (0.67.4) 212 | - React-logger (0.67.4): 213 | - glog 214 | - react-native-safe-area-context (4.5.0): 215 | - RCT-Folly 216 | - RCTRequired 217 | - RCTTypeSafety 218 | - React-Core 219 | - ReactCommon/turbomodule/core 220 | - React-perflogger (0.67.4) 221 | - React-RCTActionSheet (0.67.4): 222 | - React-Core/RCTActionSheetHeaders (= 0.67.4) 223 | - React-RCTAnimation (0.67.4): 224 | - FBReactNativeSpec (= 0.67.4) 225 | - RCT-Folly (= 2021.06.28.00-v2) 226 | - RCTTypeSafety (= 0.67.4) 227 | - React-Core/RCTAnimationHeaders (= 0.67.4) 228 | - React-jsi (= 0.67.4) 229 | - ReactCommon/turbomodule/core (= 0.67.4) 230 | - React-RCTBlob (0.67.4): 231 | - FBReactNativeSpec (= 0.67.4) 232 | - RCT-Folly (= 2021.06.28.00-v2) 233 | - React-Core/RCTBlobHeaders (= 0.67.4) 234 | - React-Core/RCTWebSocket (= 0.67.4) 235 | - React-jsi (= 0.67.4) 236 | - React-RCTNetwork (= 0.67.4) 237 | - ReactCommon/turbomodule/core (= 0.67.4) 238 | - React-RCTImage (0.67.4): 239 | - FBReactNativeSpec (= 0.67.4) 240 | - RCT-Folly (= 2021.06.28.00-v2) 241 | - RCTTypeSafety (= 0.67.4) 242 | - React-Core/RCTImageHeaders (= 0.67.4) 243 | - React-jsi (= 0.67.4) 244 | - React-RCTNetwork (= 0.67.4) 245 | - ReactCommon/turbomodule/core (= 0.67.4) 246 | - React-RCTLinking (0.67.4): 247 | - FBReactNativeSpec (= 0.67.4) 248 | - React-Core/RCTLinkingHeaders (= 0.67.4) 249 | - React-jsi (= 0.67.4) 250 | - ReactCommon/turbomodule/core (= 0.67.4) 251 | - React-RCTNetwork (0.67.4): 252 | - FBReactNativeSpec (= 0.67.4) 253 | - RCT-Folly (= 2021.06.28.00-v2) 254 | - RCTTypeSafety (= 0.67.4) 255 | - React-Core/RCTNetworkHeaders (= 0.67.4) 256 | - React-jsi (= 0.67.4) 257 | - ReactCommon/turbomodule/core (= 0.67.4) 258 | - React-RCTSettings (0.67.4): 259 | - FBReactNativeSpec (= 0.67.4) 260 | - RCT-Folly (= 2021.06.28.00-v2) 261 | - RCTTypeSafety (= 0.67.4) 262 | - React-Core/RCTSettingsHeaders (= 0.67.4) 263 | - React-jsi (= 0.67.4) 264 | - ReactCommon/turbomodule/core (= 0.67.4) 265 | - React-RCTText (0.67.4): 266 | - React-Core/RCTTextHeaders (= 0.67.4) 267 | - React-RCTVibration (0.67.4): 268 | - FBReactNativeSpec (= 0.67.4) 269 | - RCT-Folly (= 2021.06.28.00-v2) 270 | - React-Core/RCTVibrationHeaders (= 0.67.4) 271 | - React-jsi (= 0.67.4) 272 | - ReactCommon/turbomodule/core (= 0.67.4) 273 | - React-runtimeexecutor (0.67.4): 274 | - React-jsi (= 0.67.4) 275 | - ReactCommon/turbomodule/core (0.67.4): 276 | - DoubleConversion 277 | - glog 278 | - RCT-Folly (= 2021.06.28.00-v2) 279 | - React-callinvoker (= 0.67.4) 280 | - React-Core (= 0.67.4) 281 | - React-cxxreact (= 0.67.4) 282 | - React-jsi (= 0.67.4) 283 | - React-logger (= 0.67.4) 284 | - React-perflogger (= 0.67.4) 285 | - RNKeyboardInsets (1.3.2): 286 | - React-Core 287 | - Yoga (1.14.0) 288 | 289 | DEPENDENCIES: 290 | - boost (from `../../node_modules/react-native/third-party-podspecs/boost.podspec`) 291 | - DoubleConversion (from `../../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`) 292 | - FBLazyVector (from `../../node_modules/react-native/Libraries/FBLazyVector`) 293 | - FBReactNativeSpec (from `../../node_modules/react-native/React/FBReactNativeSpec`) 294 | - glog (from `../../node_modules/react-native/third-party-podspecs/glog.podspec`) 295 | - HybridNavigation (from `../../node_modules/hybrid-navigation`) 296 | - RCT-Folly (from `../../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`) 297 | - RCTRequired (from `../../node_modules/react-native/Libraries/RCTRequired`) 298 | - RCTTypeSafety (from `../../node_modules/react-native/Libraries/TypeSafety`) 299 | - React (from `../../node_modules/react-native/`) 300 | - React-callinvoker (from `../../node_modules/react-native/ReactCommon/callinvoker`) 301 | - React-Core (from `../../node_modules/react-native/`) 302 | - React-Core/DevSupport (from `../../node_modules/react-native/`) 303 | - React-Core/RCTWebSocket (from `../../node_modules/react-native/`) 304 | - React-CoreModules (from `../../node_modules/react-native/React/CoreModules`) 305 | - React-cxxreact (from `../../node_modules/react-native/ReactCommon/cxxreact`) 306 | - React-jsi (from `../../node_modules/react-native/ReactCommon/jsi`) 307 | - React-jsiexecutor (from `../../node_modules/react-native/ReactCommon/jsiexecutor`) 308 | - React-jsinspector (from `../../node_modules/react-native/ReactCommon/jsinspector`) 309 | - React-logger (from `../../node_modules/react-native/ReactCommon/logger`) 310 | - react-native-safe-area-context (from `../../node_modules/react-native-safe-area-context`) 311 | - React-perflogger (from `../../node_modules/react-native/ReactCommon/reactperflogger`) 312 | - React-RCTActionSheet (from `../../node_modules/react-native/Libraries/ActionSheetIOS`) 313 | - React-RCTAnimation (from `../../node_modules/react-native/Libraries/NativeAnimation`) 314 | - React-RCTBlob (from `../../node_modules/react-native/Libraries/Blob`) 315 | - React-RCTImage (from `../../node_modules/react-native/Libraries/Image`) 316 | - React-RCTLinking (from `../../node_modules/react-native/Libraries/LinkingIOS`) 317 | - React-RCTNetwork (from `../../node_modules/react-native/Libraries/Network`) 318 | - React-RCTSettings (from `../../node_modules/react-native/Libraries/Settings`) 319 | - React-RCTText (from `../../node_modules/react-native/Libraries/Text`) 320 | - React-RCTVibration (from `../../node_modules/react-native/Libraries/Vibration`) 321 | - React-runtimeexecutor (from `../../node_modules/react-native/ReactCommon/runtimeexecutor`) 322 | - ReactCommon/turbomodule/core (from `../../node_modules/react-native/ReactCommon`) 323 | - RNKeyboardInsets (from `../../`) 324 | - Yoga (from `../../node_modules/react-native/ReactCommon/yoga`) 325 | 326 | SPEC REPOS: 327 | trunk: 328 | - fmt 329 | 330 | EXTERNAL SOURCES: 331 | boost: 332 | :podspec: "../../node_modules/react-native/third-party-podspecs/boost.podspec" 333 | DoubleConversion: 334 | :podspec: "../../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec" 335 | FBLazyVector: 336 | :path: "../../node_modules/react-native/Libraries/FBLazyVector" 337 | FBReactNativeSpec: 338 | :path: "../../node_modules/react-native/React/FBReactNativeSpec" 339 | glog: 340 | :podspec: "../../node_modules/react-native/third-party-podspecs/glog.podspec" 341 | HybridNavigation: 342 | :path: "../../node_modules/hybrid-navigation" 343 | RCT-Folly: 344 | :podspec: "../../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec" 345 | RCTRequired: 346 | :path: "../../node_modules/react-native/Libraries/RCTRequired" 347 | RCTTypeSafety: 348 | :path: "../../node_modules/react-native/Libraries/TypeSafety" 349 | React: 350 | :path: "../../node_modules/react-native/" 351 | React-callinvoker: 352 | :path: "../../node_modules/react-native/ReactCommon/callinvoker" 353 | React-Core: 354 | :path: "../../node_modules/react-native/" 355 | React-CoreModules: 356 | :path: "../../node_modules/react-native/React/CoreModules" 357 | React-cxxreact: 358 | :path: "../../node_modules/react-native/ReactCommon/cxxreact" 359 | React-jsi: 360 | :path: "../../node_modules/react-native/ReactCommon/jsi" 361 | React-jsiexecutor: 362 | :path: "../../node_modules/react-native/ReactCommon/jsiexecutor" 363 | React-jsinspector: 364 | :path: "../../node_modules/react-native/ReactCommon/jsinspector" 365 | React-logger: 366 | :path: "../../node_modules/react-native/ReactCommon/logger" 367 | react-native-safe-area-context: 368 | :path: "../../node_modules/react-native-safe-area-context" 369 | React-perflogger: 370 | :path: "../../node_modules/react-native/ReactCommon/reactperflogger" 371 | React-RCTActionSheet: 372 | :path: "../../node_modules/react-native/Libraries/ActionSheetIOS" 373 | React-RCTAnimation: 374 | :path: "../../node_modules/react-native/Libraries/NativeAnimation" 375 | React-RCTBlob: 376 | :path: "../../node_modules/react-native/Libraries/Blob" 377 | React-RCTImage: 378 | :path: "../../node_modules/react-native/Libraries/Image" 379 | React-RCTLinking: 380 | :path: "../../node_modules/react-native/Libraries/LinkingIOS" 381 | React-RCTNetwork: 382 | :path: "../../node_modules/react-native/Libraries/Network" 383 | React-RCTSettings: 384 | :path: "../../node_modules/react-native/Libraries/Settings" 385 | React-RCTText: 386 | :path: "../../node_modules/react-native/Libraries/Text" 387 | React-RCTVibration: 388 | :path: "../../node_modules/react-native/Libraries/Vibration" 389 | React-runtimeexecutor: 390 | :path: "../../node_modules/react-native/ReactCommon/runtimeexecutor" 391 | ReactCommon: 392 | :path: "../../node_modules/react-native/ReactCommon" 393 | RNKeyboardInsets: 394 | :path: "../../" 395 | Yoga: 396 | :path: "../../node_modules/react-native/ReactCommon/yoga" 397 | 398 | SPEC CHECKSUMS: 399 | boost: a7c83b31436843459a1961bfd74b96033dc77234 400 | DoubleConversion: 831926d9b8bf8166fd87886c4abab286c2422662 401 | FBLazyVector: f7b0632c6437e312acf6349288d9aa4cb6d59030 402 | FBReactNativeSpec: 0f4e1f4cfeace095694436e7c7fcc5bf4b03a0ff 403 | fmt: ff9d55029c625d3757ed641535fd4a75fedc7ce9 404 | glog: 85ecdd10ee8d8ec362ef519a6a45ff9aa27b2e85 405 | HybridNavigation: d54e779a0f7f1b2489be3d6b1a8ad3b7616dadbb 406 | RCT-Folly: 803a9cfd78114b2ec0f140cfa6fa2a6bafb2d685 407 | RCTRequired: 0aa6c1c27e1d65920df35ceea5341a5fe76bdb79 408 | RCTTypeSafety: d76a59d00632891e11ed7522dba3fd1a995e573a 409 | React: ab8c09da2e7704f4b3ebad4baa6cfdfcc852dcb5 410 | React-callinvoker: 216fb96b482da516b8aba4142b145938f6ea92f0 411 | React-Core: af99b93aff83599485e0e0879879aafa35ceae32 412 | React-CoreModules: 137a054ce8c547e81dc3502933b1bc0fd08df05d 413 | React-cxxreact: ec5ee6b08664f5b8ac71d8ad912f54d540c4f817 414 | React-jsi: 3e084c80fd364cee64668d5df46d40c39f7973e1 415 | React-jsiexecutor: cbdf37cebdc4f5d8b3d0bf5ccaa6147fd9de9f3d 416 | React-jsinspector: f4775ea9118cbe1f72b834f0f842baa7a99508d8 417 | React-logger: a1f028f6d8639a3f364ef80419e5e862e1115250 418 | react-native-safe-area-context: 39c2d8be3328df5d437ac1700f4f3a4f75716acc 419 | React-perflogger: 0afaf2f01a47fd0fc368a93bfbb5bd3b26db6e7f 420 | React-RCTActionSheet: 59f35c4029e0b532fc42114241a06e170b7431a2 421 | React-RCTAnimation: aae4f4bed122e78bdab72f7118d291d70a932ce2 422 | React-RCTBlob: f6fb23394b4f28cd86fa7e9f5f6ae45c23669fda 423 | React-RCTImage: 638815cf96124386dd296067246d91441932ae3f 424 | React-RCTLinking: 254dd06283dd6fdb784285f95e7cec8053c3270f 425 | React-RCTNetwork: 8a4c2d4f357268e520b060572d02bc69a9b991fb 426 | React-RCTSettings: 35d44cbb9972ab933bd0a59ea3e6646dcb030ba3 427 | React-RCTText: cc5315df8458cfa7b537e621271ef43273955a97 428 | React-RCTVibration: 3b52a7dced19cdb025b4f88ab26ceb2d85f30ba2 429 | React-runtimeexecutor: a9d3c82ddf7ffdad9fbe6a81c6d6f8c06385464d 430 | ReactCommon: 07d0c460b9ba9af3eaf1b8f5abe7daaad28c9c4e 431 | RNKeyboardInsets: beeec3dc406d040558bf8c9ba6a6c5f717a3268a 432 | Yoga: d6b6a80659aa3e91aaba01d0012e7edcbedcbecd 433 | 434 | PODFILE CHECKSUM: 5f8e27516be9e29e667680d1a90470ffeb5a4037 435 | 436 | COCOAPODS: 1.11.3 437 | -------------------------------------------------------------------------------- /ios/KeyboardInsets.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 48; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 91C884A6202C3E8600EC0A20 /* undefined.m in Sources */ = {isa = PBXBuildFile; fileRef = 91C884A1202C3E8600EC0A20 /* undefined.m */; }; 11 | /* End PBXBuildFile section */ 12 | 13 | /* Begin PBXCopyFilesBuildPhase section */ 14 | 910362D01FE9318600F4DA8E /* CopyFiles */ = { 15 | isa = PBXCopyFilesBuildPhase; 16 | buildActionMask = 2147483647; 17 | dstPath = "include/$(PRODUCT_NAME)"; 18 | dstSubfolderSpec = 16; 19 | files = ( 20 | ); 21 | runOnlyForDeploymentPostprocessing = 0; 22 | }; 23 | /* End PBXCopyFilesBuildPhase section */ 24 | 25 | /* Begin PBXFileReference section */ 26 | 910362D21FE9318600F4DA8E /* libKeyboardInsets.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libKeyboardInsets.a; sourceTree = BUILT_PRODUCTS_DIR; }; 27 | 91C884A1202C3E8600EC0A20 /* undefined.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = undefined.m; sourceTree = ""; }; 28 | 91C884A2202C3E8600EC0A20 /* undefined.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = undefined.h; sourceTree = ""; }; 29 | /* End PBXFileReference section */ 30 | 31 | /* Begin PBXFrameworksBuildPhase section */ 32 | 910362CF1FE9318600F4DA8E /* Frameworks */ = { 33 | isa = PBXFrameworksBuildPhase; 34 | buildActionMask = 2147483647; 35 | files = ( 36 | ); 37 | runOnlyForDeploymentPostprocessing = 0; 38 | }; 39 | /* End PBXFrameworksBuildPhase section */ 40 | 41 | /* Begin PBXGroup section */ 42 | 910362C91FE9318600F4DA8E = { 43 | isa = PBXGroup; 44 | children = ( 45 | 910362D41FE9318600F4DA8E /* KeyboardInsets */, 46 | 910362D31FE9318600F4DA8E /* Products */, 47 | ); 48 | sourceTree = ""; 49 | }; 50 | 910362D31FE9318600F4DA8E /* Products */ = { 51 | isa = PBXGroup; 52 | children = ( 53 | 910362D21FE9318600F4DA8E /* libKeyboardInsets.a */, 54 | ); 55 | name = Products; 56 | sourceTree = ""; 57 | }; 58 | 910362D41FE9318600F4DA8E /* KeyboardInsets */ = { 59 | isa = PBXGroup; 60 | children = ( 61 | 91C884A2202C3E8600EC0A20 /* undefined.h */, 62 | 91C884A1202C3E8600EC0A20 /* undefined.m */, 63 | ); 64 | path = KeyboardInsets; 65 | sourceTree = ""; 66 | }; 67 | /* End PBXGroup section */ 68 | 69 | /* Begin PBXNativeTarget section */ 70 | 910362D11FE9318600F4DA8E /* KeyboardInsets */ = { 71 | isa = PBXNativeTarget; 72 | buildConfigurationList = 910362DB1FE9318600F4DA8E /* Build configuration list for PBXNativeTarget "KeyboardInsets" */; 73 | buildPhases = ( 74 | 910362CE1FE9318600F4DA8E /* Sources */, 75 | 910362CF1FE9318600F4DA8E /* Frameworks */, 76 | 910362D01FE9318600F4DA8E /* CopyFiles */, 77 | ); 78 | buildRules = ( 79 | ); 80 | dependencies = ( 81 | ); 82 | name = KeyboardInsets; 83 | productName = KeyboardInsets; 84 | productReference = 910362D21FE9318600F4DA8E /* libKeyboardInsets.a */; 85 | productType = "com.apple.product-type.library.static"; 86 | }; 87 | /* End PBXNativeTarget section */ 88 | 89 | /* Begin PBXProject section */ 90 | 910362CA1FE9318600F4DA8E /* Project object */ = { 91 | isa = PBXProject; 92 | attributes = { 93 | LastUpgradeCheck = 0920; 94 | ORGANIZATIONNAME = Listen; 95 | TargetAttributes = { 96 | 910362D11FE9318600F4DA8E = { 97 | CreatedOnToolsVersion = 9.2; 98 | ProvisioningStyle = Automatic; 99 | }; 100 | }; 101 | }; 102 | buildConfigurationList = 910362CD1FE9318600F4DA8E /* Build configuration list for PBXProject "KeyboardInsets" */; 103 | compatibilityVersion = "Xcode 8.0"; 104 | developmentRegion = en; 105 | hasScannedForEncodings = 0; 106 | knownRegions = ( 107 | en, 108 | ); 109 | mainGroup = 910362C91FE9318600F4DA8E; 110 | productRefGroup = 910362D31FE9318600F4DA8E /* Products */; 111 | projectDirPath = ""; 112 | projectRoot = ""; 113 | targets = ( 114 | 910362D11FE9318600F4DA8E /* KeyboardInsets */, 115 | ); 116 | }; 117 | /* End PBXProject section */ 118 | 119 | /* Begin PBXSourcesBuildPhase section */ 120 | 910362CE1FE9318600F4DA8E /* Sources */ = { 121 | isa = PBXSourcesBuildPhase; 122 | buildActionMask = 2147483647; 123 | files = ( 124 | 91C884A6202C3E8600EC0A20 /* undefined.m in Sources */, 125 | ); 126 | runOnlyForDeploymentPostprocessing = 0; 127 | }; 128 | /* End PBXSourcesBuildPhase section */ 129 | 130 | /* Begin XCBuildConfiguration section */ 131 | 910362D91FE9318600F4DA8E /* Debug */ = { 132 | isa = XCBuildConfiguration; 133 | buildSettings = { 134 | ALWAYS_SEARCH_USER_PATHS = NO; 135 | CLANG_ANALYZER_NONNULL = YES; 136 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 137 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 138 | CLANG_CXX_LIBRARY = "libc++"; 139 | CLANG_ENABLE_MODULES = YES; 140 | CLANG_ENABLE_OBJC_ARC = YES; 141 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 142 | CLANG_WARN_BOOL_CONVERSION = YES; 143 | CLANG_WARN_COMMA = YES; 144 | CLANG_WARN_CONSTANT_CONVERSION = YES; 145 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 146 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 147 | CLANG_WARN_EMPTY_BODY = YES; 148 | CLANG_WARN_ENUM_CONVERSION = YES; 149 | CLANG_WARN_INFINITE_RECURSION = YES; 150 | CLANG_WARN_INT_CONVERSION = YES; 151 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 152 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 153 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 154 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 155 | CLANG_WARN_STRICT_PROTOTYPES = YES; 156 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 157 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 158 | CLANG_WARN_UNREACHABLE_CODE = YES; 159 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 160 | CODE_SIGN_IDENTITY = "iPhone Developer"; 161 | COPY_PHASE_STRIP = NO; 162 | DEBUG_INFORMATION_FORMAT = dwarf; 163 | ENABLE_STRICT_OBJC_MSGSEND = YES; 164 | ENABLE_TESTABILITY = YES; 165 | GCC_C_LANGUAGE_STANDARD = gnu11; 166 | GCC_DYNAMIC_NO_PIC = NO; 167 | GCC_NO_COMMON_BLOCKS = YES; 168 | GCC_OPTIMIZATION_LEVEL = 0; 169 | GCC_PREPROCESSOR_DEFINITIONS = ( 170 | "DEBUG=1", 171 | "$(inherited)", 172 | ); 173 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 174 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 175 | GCC_WARN_UNDECLARED_SELECTOR = YES; 176 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 177 | GCC_WARN_UNUSED_FUNCTION = YES; 178 | GCC_WARN_UNUSED_VARIABLE = YES; 179 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 180 | MTL_ENABLE_DEBUG_INFO = YES; 181 | ONLY_ACTIVE_ARCH = YES; 182 | SDKROOT = iphoneos; 183 | }; 184 | name = Debug; 185 | }; 186 | 910362DA1FE9318600F4DA8E /* Release */ = { 187 | isa = XCBuildConfiguration; 188 | buildSettings = { 189 | ALWAYS_SEARCH_USER_PATHS = NO; 190 | CLANG_ANALYZER_NONNULL = YES; 191 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 192 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 193 | CLANG_CXX_LIBRARY = "libc++"; 194 | CLANG_ENABLE_MODULES = YES; 195 | CLANG_ENABLE_OBJC_ARC = YES; 196 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 197 | CLANG_WARN_BOOL_CONVERSION = YES; 198 | CLANG_WARN_COMMA = YES; 199 | CLANG_WARN_CONSTANT_CONVERSION = YES; 200 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 201 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 202 | CLANG_WARN_EMPTY_BODY = YES; 203 | CLANG_WARN_ENUM_CONVERSION = YES; 204 | CLANG_WARN_INFINITE_RECURSION = YES; 205 | CLANG_WARN_INT_CONVERSION = YES; 206 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 207 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 208 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 209 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 210 | CLANG_WARN_STRICT_PROTOTYPES = YES; 211 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 212 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 213 | CLANG_WARN_UNREACHABLE_CODE = YES; 214 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 215 | CODE_SIGN_IDENTITY = "iPhone Developer"; 216 | COPY_PHASE_STRIP = NO; 217 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 218 | ENABLE_NS_ASSERTIONS = NO; 219 | ENABLE_STRICT_OBJC_MSGSEND = YES; 220 | GCC_C_LANGUAGE_STANDARD = gnu11; 221 | GCC_NO_COMMON_BLOCKS = YES; 222 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 223 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 224 | GCC_WARN_UNDECLARED_SELECTOR = YES; 225 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 226 | GCC_WARN_UNUSED_FUNCTION = YES; 227 | GCC_WARN_UNUSED_VARIABLE = YES; 228 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 229 | MTL_ENABLE_DEBUG_INFO = NO; 230 | SDKROOT = iphoneos; 231 | VALIDATE_PRODUCT = YES; 232 | }; 233 | name = Release; 234 | }; 235 | 910362DC1FE9318600F4DA8E /* Debug */ = { 236 | isa = XCBuildConfiguration; 237 | buildSettings = { 238 | CODE_SIGN_STYLE = Automatic; 239 | DEVELOPMENT_TEAM = 9H9696K6NL; 240 | OTHER_LDFLAGS = "-ObjC"; 241 | PRODUCT_NAME = "$(TARGET_NAME)"; 242 | SKIP_INSTALL = YES; 243 | TARGETED_DEVICE_FAMILY = "1,2"; 244 | }; 245 | name = Debug; 246 | }; 247 | 910362DD1FE9318600F4DA8E /* Release */ = { 248 | isa = XCBuildConfiguration; 249 | buildSettings = { 250 | CODE_SIGN_STYLE = Automatic; 251 | DEVELOPMENT_TEAM = 9H9696K6NL; 252 | OTHER_LDFLAGS = "-ObjC"; 253 | PRODUCT_NAME = "$(TARGET_NAME)"; 254 | SKIP_INSTALL = YES; 255 | TARGETED_DEVICE_FAMILY = "1,2"; 256 | }; 257 | name = Release; 258 | }; 259 | /* End XCBuildConfiguration section */ 260 | 261 | /* Begin XCConfigurationList section */ 262 | 910362CD1FE9318600F4DA8E /* Build configuration list for PBXProject "KeyboardInsets" */ = { 263 | isa = XCConfigurationList; 264 | buildConfigurations = ( 265 | 910362D91FE9318600F4DA8E /* Debug */, 266 | 910362DA1FE9318600F4DA8E /* Release */, 267 | ); 268 | defaultConfigurationIsVisible = 0; 269 | defaultConfigurationName = Release; 270 | }; 271 | 910362DB1FE9318600F4DA8E /* Build configuration list for PBXNativeTarget "KeyboardInsets" */ = { 272 | isa = XCConfigurationList; 273 | buildConfigurations = ( 274 | 910362DC1FE9318600F4DA8E /* Debug */, 275 | 910362DD1FE9318600F4DA8E /* Release */, 276 | ); 277 | defaultConfigurationIsVisible = 0; 278 | defaultConfigurationName = Release; 279 | }; 280 | /* End XCConfigurationList section */ 281 | }; 282 | rootObject = 910362CA1FE9318600F4DA8E /* Project object */; 283 | } 284 | -------------------------------------------------------------------------------- /ios/KeyboardInsets/HBDKeyboardAutoHandler.h: -------------------------------------------------------------------------------- 1 | #import "HBDKeyboardInsetsView.h" 2 | 3 | NS_ASSUME_NONNULL_BEGIN 4 | 5 | @interface HBDKeyboardAutoHandler : NSObject 6 | 7 | - (instancetype)initWithKeyboardInsetsView:(HBDKeyboardInsetsView *)view; 8 | 9 | @end 10 | 11 | NS_ASSUME_NONNULL_END 12 | -------------------------------------------------------------------------------- /ios/KeyboardInsets/HBDKeyboardAutoHandler.m: -------------------------------------------------------------------------------- 1 | #import "HBDKeyboardAutoHandler.h" 2 | 3 | #import 4 | #import 5 | #import 6 | 7 | @interface HBDKeyboardAutoHandler () 8 | 9 | @property (nonatomic, weak) HBDKeyboardInsetsView *view; 10 | @property (nonatomic, assign) CGFloat keyboardHeight; 11 | @property (nonatomic, assign) BOOL forceUpdated; 12 | @property (nonatomic, assign) CGFloat edgeBottom; 13 | @property (nonatomic, assign) BOOL shown; 14 | 15 | @end 16 | 17 | @implementation HBDKeyboardAutoHandler 18 | 19 | - (instancetype)initWithKeyboardInsetsView:(HBDKeyboardInsetsView *)view { 20 | if (self = [super init]) { 21 | _view = view; 22 | } 23 | return self; 24 | } 25 | 26 | - (void)keyboardWillShow:(UIView *)focusView keyboardHeight:(CGFloat)keyboardHeight { 27 | self.shown = YES; 28 | if (self.keyboardHeight != keyboardHeight) { 29 | self.forceUpdated = YES; 30 | } 31 | self.keyboardHeight = keyboardHeight; 32 | 33 | [self adjustScrollViewOffsetIfNeeded:focusView]; 34 | [self refreshEdgeBottom:focusView]; 35 | } 36 | 37 | - (void)keyboardDidShow:(UIView *)focusView keyboardHeight:(CGFloat)keyboardHeight { 38 | if (focusView) { 39 | [self handleKeyboardTransition:keyboardHeight]; 40 | } else { 41 | self.forceUpdated = YES; 42 | [self handleKeyboardTransition:0]; 43 | } 44 | } 45 | 46 | - (void)keyboardWillHide:(UIView *)focusView keyboardHeight:(CGFloat)keyboardHeight { 47 | self.shown = NO; 48 | } 49 | 50 | - (void)keyboardDidHide:(UIView *)focusView keyboardHeight:(CGFloat)keyboardHeight { 51 | [self handleKeyboardTransition:0]; 52 | self.edgeBottom = 0; 53 | } 54 | 55 | - (void)handleKeyboardTransition:(CGFloat)position { 56 | HBDKeyboardInsetsView *view = self.view; 57 | 58 | CGFloat translationY = 0; 59 | if (position > 0) { 60 | CGFloat edgeBottom = MAX(self.edgeBottom - view.extraHeight, 0); 61 | translationY = -MAX(position - edgeBottom, 0); 62 | } 63 | 64 | if (self.forceUpdated) { 65 | self.forceUpdated = NO; 66 | view.transform = CGAffineTransformMakeTranslation(0, translationY); 67 | } 68 | 69 | if (self.shown && view.transform.ty < translationY) { 70 | return; 71 | } 72 | 73 | view.transform = CGAffineTransformMakeTranslation(0, translationY); 74 | } 75 | 76 | - (void)refreshEdgeBottom:(UIView *)focusView { 77 | UIView *view = self.view; 78 | CGFloat translateY = view.transform.ty; 79 | CGRect windowFrame = [view.window convertRect:focusView.frame fromView:focusView.superview]; 80 | CGFloat dy = CGRectGetMaxY(view.window.bounds) - CGRectGetMaxY(windowFrame); 81 | CGFloat newEdgeBottom = MAX(dy + translateY, 0); 82 | if (self.edgeBottom == 0 || self.edgeBottom != newEdgeBottom){ 83 | self.edgeBottom = newEdgeBottom; 84 | } 85 | } 86 | 87 | - (void)adjustScrollViewOffsetIfNeeded:(UIView *)focusView { 88 | RCTScrollView *rct = [HBDKeyboardAutoHandler findClosetScrollView:focusView]; 89 | if (rct) { 90 | UIScrollView *scrollView = rct.scrollView; 91 | CGRect frame = [rct.contentView convertRect:focusView.frame fromView:focusView.superview]; 92 | CGFloat dy = CGRectGetHeight(rct.frame) + scrollView.contentOffset.y - CGRectGetMaxY(frame) - self.view.extraHeight; 93 | if (dy < 0) { 94 | CGFloat range = scrollView.contentSize.height - scrollView.frame.size.height; 95 | CGPoint offset = scrollView.contentOffset; 96 | offset.y = MIN(range, offset.y - dy); 97 | [rct scrollToOffset:offset animated:NO]; 98 | } 99 | } 100 | } 101 | 102 | + (RCTScrollView *)findClosetScrollView:(UIView *)view { 103 | if ([view isKindOfClass:[RCTScrollView class]]) { 104 | return (RCTScrollView *)view; 105 | } 106 | 107 | if (view.superview) { 108 | return [self findClosetScrollView:view.superview]; 109 | } 110 | 111 | return nil; 112 | } 113 | 114 | @end 115 | -------------------------------------------------------------------------------- /ios/KeyboardInsets/HBDKeyboardInsetsModule.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | NS_ASSUME_NONNULL_BEGIN 4 | 5 | @interface HBDKeyboardInsetsModule : NSObject 6 | 7 | @end 8 | 9 | NS_ASSUME_NONNULL_END 10 | -------------------------------------------------------------------------------- /ios/KeyboardInsets/HBDKeyboardInsetsModule.m: -------------------------------------------------------------------------------- 1 | #import "HBDKeyboardInsetsModule.h" 2 | #import 3 | #import 4 | 5 | @implementation HBDKeyboardInsetsModule 6 | 7 | RCT_EXPORT_MODULE(KeyboardInsetsModule) 8 | 9 | @synthesize bridge; 10 | 11 | + (BOOL)requiresMainQueueSetup { 12 | return YES; 13 | } 14 | 15 | - (dispatch_queue_t)methodQueue { 16 | return dispatch_get_main_queue(); 17 | } 18 | 19 | RCT_EXPORT_METHOD(getEdgeInsetsForView:(nonnull NSNumber *)reactTag callback:(RCTResponseSenderBlock)callback) { 20 | RCTUIManager* uiManager = [self.bridge moduleForClass:[RCTUIManager class]]; 21 | UIView* view = [uiManager viewForReactTag:reactTag]; 22 | CGRect windowFrame = [view.window convertRect:view.frame fromView:view.superview]; 23 | 24 | CGAffineTransform t = [self maybeTransform:view]; 25 | 26 | callback(@[ 27 | @{ 28 | @"left": @(MAX(0, windowFrame.origin.x + t.tx)), 29 | @"top": @(MAX(0, windowFrame.origin.y + t.ty)), 30 | @"right": @(MAX(0, CGRectGetMaxX(view.window.bounds) - CGRectGetMaxX(windowFrame) + t.tx)), 31 | @"bottom": @(MAX(0, CGRectGetMaxY(view.window.bounds) - CGRectGetMaxY(windowFrame) + t.ty)), 32 | } 33 | ]); 34 | } 35 | 36 | 37 | - (CGAffineTransform)maybeTransform:(UIView *)view { 38 | if (view.transform.ty != 0) { 39 | return view.transform; 40 | } 41 | 42 | if (!view.superview) { 43 | return CGAffineTransformIdentity; 44 | } 45 | 46 | return [self maybeTransform:view.superview]; 47 | } 48 | 49 | 50 | @end 51 | -------------------------------------------------------------------------------- /ios/KeyboardInsets/HBDKeyboardInsetsView.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | NS_ASSUME_NONNULL_BEGIN 4 | 5 | @interface HBDKeyboardInsetsView : RCTView 6 | 7 | @property(nonatomic, copy) RCTDirectEventBlock onStatusChanged; 8 | @property(nonatomic, copy) RCTDirectEventBlock onPositionChanged; 9 | 10 | @property(nonatomic, copy) NSString *mode; 11 | @property(nonatomic, assign) CGFloat extraHeight; 12 | 13 | @end 14 | 15 | @protocol HBDKeyboardHandler 16 | 17 | - (void)keyboardWillShow:(UIView *)focusView keyboardHeight:(CGFloat)keyboardHeight; 18 | 19 | - (void)keyboardDidShow:(UIView *)focusView keyboardHeight:(CGFloat)keyboardHeight; 20 | 21 | - (void)keyboardWillHide:(UIView *)focusView keyboardHeight:(CGFloat)keyboardHeight; 22 | 23 | - (void)keyboardDidHide:(UIView *)focusView keyboardHeight:(CGFloat)keyboardHeight; 24 | 25 | - (void)handleKeyboardTransition:(CGFloat)position; 26 | 27 | @end 28 | 29 | NS_ASSUME_NONNULL_END 30 | -------------------------------------------------------------------------------- /ios/KeyboardInsets/HBDKeyboardInsetsView.m: -------------------------------------------------------------------------------- 1 | #import "HBDKeyboardInsetsView.h" 2 | #import "HBDKeyboardAutoHandler.h" 3 | #import "HBDKeyboardManualHandler.h" 4 | 5 | #import 6 | #import 7 | #import 8 | 9 | @implementation HBDKeyboardInsetsView { 10 | UIView *_focusView; 11 | 12 | CADisplayLink *_displayLink; 13 | UIView *_keyboardView; 14 | CGFloat _keyboardHeight; 15 | 16 | HBDKeyboardAutoHandler *_autoHandler; 17 | HBDKeyboardManualHandler *_manualHandler; 18 | } 19 | 20 | - (instancetype)init { 21 | if (self = [super init]) { 22 | _mode = @"auto"; 23 | } 24 | return self; 25 | } 26 | 27 | - (HBDKeyboardAutoHandler *)autoHandler { 28 | if (!_autoHandler) { 29 | _autoHandler = [[HBDKeyboardAutoHandler alloc] initWithKeyboardInsetsView:self]; 30 | } 31 | return _autoHandler; 32 | } 33 | 34 | - (HBDKeyboardManualHandler *)manualHandler { 35 | if (!_manualHandler) { 36 | _manualHandler = [[HBDKeyboardManualHandler alloc] initWithKeyboardInsetsView:self]; 37 | } 38 | return _manualHandler; 39 | } 40 | 41 | - (void)willMoveToWindow:(UIWindow *)newWindow { 42 | if (!newWindow) { 43 | [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil]; 44 | [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardDidShowNotification object:nil]; 45 | [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil]; 46 | [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardDidHideNotification object:nil]; 47 | [self stopWatchKeyboardTransition]; 48 | } 49 | } 50 | 51 | - (void)didMoveToWindow { 52 | if (self.window) { 53 | [[NSNotificationCenter defaultCenter] addObserver:self 54 | selector:@selector(keyboardWillShow:) 55 | name:UIKeyboardWillShowNotification 56 | object:nil]; 57 | 58 | [[NSNotificationCenter defaultCenter] addObserver:self 59 | selector:@selector(keyboardDidShow:) 60 | name:UIKeyboardDidShowNotification 61 | object:nil]; 62 | 63 | [[NSNotificationCenter defaultCenter] addObserver:self 64 | selector:@selector(keyboardWillHide:) 65 | name:UIKeyboardWillHideNotification 66 | object:nil]; 67 | 68 | [[NSNotificationCenter defaultCenter] addObserver:self 69 | selector:@selector(keyboardDidHide:) 70 | name:UIKeyboardDidHideNotification 71 | object:nil]; 72 | } 73 | } 74 | 75 | - (void)keyboardWillShow:(NSNotification *)notification { 76 | UIView *focusView = [HBDKeyboardInsetsView findFocusView:self]; 77 | 78 | if (![self shouldHandleKeyboardTransition:focusView]) { 79 | return; 80 | } 81 | 82 | _focusView = focusView; 83 | _keyboardView = [HBDKeyboardInsetsView findKeyboardView]; 84 | 85 | NSDictionary *userInfo = [notification userInfo]; 86 | CGRect keyboardRect = [[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue]; 87 | CGFloat keyboardHeight = keyboardRect.size.height; 88 | 89 | _keyboardHeight = keyboardHeight; 90 | 91 | if ([self isAutoMode]) { 92 | [[self autoHandler] keyboardWillShow:focusView keyboardHeight:keyboardHeight]; 93 | } else { 94 | [[self manualHandler] keyboardWillShow:focusView keyboardHeight:keyboardHeight]; 95 | } 96 | 97 | RCTLogInfo(@"[KeyboardInsetsView] keyboardWillShow startWatchKeyboardTransition"); 98 | [self startWatchKeyboardTransition]; 99 | } 100 | 101 | - (void)keyboardDidShow:(NSNotification *)notification { 102 | if (![self shouldHandleKeyboardTransition:_focusView]) { 103 | return; 104 | } 105 | 106 | RCTLogInfo(@"[KeyboardInsetsView] keyboardDidShow stopWatchKeyboardTransition"); 107 | [self stopWatchKeyboardTransition]; 108 | 109 | if ([self isAutoMode]) { 110 | UIView *focusView = [HBDKeyboardInsetsView findFocusView:self]; 111 | if (focusView && focusView != _focusView) { 112 | HBDKeyboardInsetsView *insetsView = [HBDKeyboardInsetsView findClosetKeyboardInsetsView:focusView]; 113 | if (insetsView != self) { 114 | focusView = nil; 115 | } 116 | } 117 | _focusView = focusView; 118 | [[self autoHandler] keyboardDidShow:focusView keyboardHeight:_keyboardHeight]; 119 | } else { 120 | [[self manualHandler] keyboardDidShow:_focusView keyboardHeight:_keyboardHeight]; 121 | } 122 | } 123 | 124 | - (void)keyboardWillHide:(NSNotification *)notification { 125 | if (![self shouldHandleKeyboardTransition:_focusView]) { 126 | return; 127 | } 128 | 129 | _keyboardView = [HBDKeyboardInsetsView findKeyboardView]; 130 | 131 | if ([self isAutoMode]) { 132 | [[self autoHandler] keyboardWillHide:_focusView keyboardHeight:_keyboardHeight]; 133 | } else { 134 | [[self manualHandler] keyboardWillHide:_focusView keyboardHeight:_keyboardHeight]; 135 | } 136 | 137 | RCTLogInfo(@"[KeyboardInsetsView] keyboardWillHide startWatchKeyboardTransition"); 138 | [self startWatchKeyboardTransition]; 139 | } 140 | 141 | 142 | - (void)keyboardDidHide:(NSNotification *)notification { 143 | UIView *focusView = _focusView; 144 | _focusView = nil; 145 | 146 | if (![self shouldHandleKeyboardTransition:focusView]) { 147 | return; 148 | } 149 | 150 | RCTLogInfo(@"[KeyboardInsetsView] keyboardDidHide stopWatchKeyboardTransition"); 151 | [self stopWatchKeyboardTransition]; 152 | 153 | if ([self isAutoMode]) { 154 | [[self autoHandler] keyboardDidHide:focusView keyboardHeight:_keyboardHeight]; 155 | } else { 156 | [[self manualHandler] keyboardDidHide:focusView keyboardHeight:_keyboardHeight]; 157 | } 158 | } 159 | 160 | - (BOOL)shouldHandleKeyboardTransition:(UIView *)focusView { 161 | if (focusView) { 162 | HBDKeyboardInsetsView *closet = [HBDKeyboardInsetsView findClosetKeyboardInsetsView:focusView]; 163 | return closet == self; 164 | } 165 | return NO; 166 | } 167 | 168 | 169 | - (void)startWatchKeyboardTransition { 170 | [self stopWatchKeyboardTransition]; 171 | _displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(watchKeyboardTransition)]; 172 | _displayLink.preferredFramesPerSecond = 120; 173 | [_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes]; 174 | } 175 | 176 | - (void)stopWatchKeyboardTransition { 177 | if(_displayLink){ 178 | [_displayLink invalidate]; 179 | _displayLink = nil; 180 | } 181 | } 182 | 183 | - (void)watchKeyboardTransition { 184 | if (_keyboardView == nil) { 185 | return; 186 | } 187 | 188 | CGFloat keyboardFrameY = [_keyboardView.layer presentationLayer].frame.origin.y; 189 | CGFloat keyboardWindowH = _keyboardView.window.bounds.size.height; 190 | [self handleKeyboardTransition:(keyboardWindowH - keyboardFrameY)]; 191 | } 192 | 193 | - (void)handleKeyboardTransition:(CGFloat)position { 194 | if ([self isAutoMode]) { 195 | if (_focusView) { 196 | [[self autoHandler] handleKeyboardTransition:position]; 197 | } 198 | } else { 199 | [[self manualHandler] handleKeyboardTransition:position]; 200 | } 201 | } 202 | 203 | - (BOOL)isAutoMode { 204 | return [self.mode isEqualToString:@"auto"]; 205 | } 206 | 207 | + (UIView *)findKeyboardView { 208 | NSArray *windows = UIApplication.sharedApplication.windows; 209 | for (UIWindow *window in windows) { 210 | if ([window.description hasPrefix:@" 2 | 3 | NS_ASSUME_NONNULL_BEGIN 4 | 5 | @interface HBDKeyboardInsetsViewManager : RCTViewManager 6 | 7 | @end 8 | 9 | NS_ASSUME_NONNULL_END 10 | -------------------------------------------------------------------------------- /ios/KeyboardInsets/HBDKeyboardInsetsViewManager.m: -------------------------------------------------------------------------------- 1 | #import "HBDKeyboardInsetsViewManager.h" 2 | #import "HBDKeyboardInsetsView.h" 3 | 4 | @implementation HBDKeyboardInsetsViewManager 5 | 6 | RCT_EXPORT_MODULE(KeyboardInsetsView) 7 | 8 | - (UIView *)view { 9 | return [[HBDKeyboardInsetsView alloc] init]; 10 | } 11 | 12 | RCT_EXPORT_VIEW_PROPERTY(mode, NSString) 13 | RCT_EXPORT_VIEW_PROPERTY(extraHeight, CGFloat) 14 | 15 | RCT_EXPORT_VIEW_PROPERTY(onStatusChanged, RCTDirectEventBlock) 16 | RCT_EXPORT_VIEW_PROPERTY(onPositionChanged, RCTDirectEventBlock) 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /ios/KeyboardInsets/HBDKeyboardManualHandler.h: -------------------------------------------------------------------------------- 1 | #import "HBDKeyboardInsetsView.h" 2 | 3 | NS_ASSUME_NONNULL_BEGIN 4 | 5 | @interface HBDKeyboardManualHandler : NSObject 6 | 7 | - (instancetype)initWithKeyboardInsetsView:(HBDKeyboardInsetsView *)view; 8 | 9 | @end 10 | 11 | NS_ASSUME_NONNULL_END 12 | -------------------------------------------------------------------------------- /ios/KeyboardInsets/HBDKeyboardManualHandler.m: -------------------------------------------------------------------------------- 1 | #import "HBDKeyboardManualHandler.h" 2 | 3 | #import 4 | #import 5 | #import 6 | 7 | @interface HBDKeyboardManualHandler () 8 | 9 | @property (nonatomic, weak) HBDKeyboardInsetsView *view; 10 | 11 | @end 12 | 13 | @implementation HBDKeyboardManualHandler 14 | 15 | - (instancetype)initWithKeyboardInsetsView:(HBDKeyboardInsetsView *)view { 16 | if (self = [super init]) { 17 | _view = view; 18 | } 19 | return self; 20 | } 21 | 22 | - (void)keyboardWillShow:(UIView *)focusView keyboardHeight:(CGFloat)keyboardHeight { 23 | self.view.onStatusChanged(@{ 24 | @"height": @(keyboardHeight), 25 | @"shown": @(YES), 26 | @"transitioning": @(YES), 27 | }); 28 | } 29 | 30 | - (void)keyboardDidShow:(UIView *)focusView keyboardHeight:(CGFloat)keyboardHeight { 31 | [self handleKeyboardTransition:keyboardHeight]; 32 | self.view.onStatusChanged(@{ 33 | @"height": @(keyboardHeight), 34 | @"shown": @(YES), 35 | @"transitioning": @(NO), 36 | }); 37 | } 38 | 39 | - (void)keyboardWillHide:(UIView *)focusView keyboardHeight:(CGFloat)keyboardHeight { 40 | self.view.onStatusChanged(@{ 41 | @"height": @(keyboardHeight), 42 | @"shown": @(NO), 43 | @"transitioning": @(YES), 44 | }); 45 | } 46 | 47 | - (void)keyboardDidHide:(UIView *)focusView keyboardHeight:(CGFloat)keyboardHeight { 48 | [self handleKeyboardTransition:0]; 49 | self.view.onStatusChanged(@{ 50 | @"height": @(keyboardHeight), 51 | @"shown": @(NO), 52 | @"transitioning": @(NO), 53 | }); 54 | } 55 | 56 | - (void)handleKeyboardTransition:(CGFloat)position { 57 | RCTLogInfo(@"[KeyboardInsetsView] keyboard position: %f", position); 58 | self.view.onPositionChanged(@{ 59 | @"position": @(position) 60 | }); 61 | } 62 | 63 | @end 64 | -------------------------------------------------------------------------------- /metro.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Metro configuration for React Native 3 | * https://github.com/facebook/react-native 4 | * 5 | * @format 6 | */ 7 | 8 | module.exports = { 9 | transformer: { 10 | getTransformOptions: async () => ({ 11 | transform: { 12 | experimentalImportSupport: false, 13 | inlineRequires: false, 14 | }, 15 | }), 16 | }, 17 | } 18 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-keyboard-insets", 3 | "description": "A powerful Keyboard Aware View for React Native", 4 | "version": "1.3.5", 5 | "main": "./lib/index.js", 6 | "typings": "./lib/index.d.ts", 7 | "react-native": "src/index", 8 | "nativePackage": true, 9 | "files": [ 10 | "src", 11 | "lib", 12 | "android", 13 | "ios", 14 | "RNKeyboardInsets.podspec", 15 | "!android/build", 16 | "!android/.gradle", 17 | "!android/.idea", 18 | "!android/local.properties", 19 | "!ios/build", 20 | "!**/__tests__" 21 | ], 22 | "repository": "https://github.com/listenzz/react-native-keyboard-insets", 23 | "homepage": "https://github.com/listenzz/react-native-keyboard-insets#readme", 24 | "author": "listenzz (https://github.com/listenzz)", 25 | "license": "MIT", 26 | "keywords": [ 27 | "react-native" 28 | ], 29 | "scripts": { 30 | "build": "rm -rf ./lib && tsc -p tsconfig.build.json", 31 | "prepare": "npm run build", 32 | "tsc": "tsc", 33 | "start": "watchman watch-del-all && react-native start --reset-cache", 34 | "android": "react-native run-android", 35 | "ios": "react-native run-ios", 36 | "test": "jest", 37 | "lint": "eslint . --fix --ext .js,.jsx,.ts,.tsx" 38 | }, 39 | "peerDependencies": { 40 | "react": ">=16.8", 41 | "react-native": ">=0.60" 42 | }, 43 | "devDependencies": { 44 | "@babel/core": "^7.13.10", 45 | "@babel/runtime": "^7.13.10", 46 | "@gfez/eslint-config-react-native": "^1.0.0", 47 | "@types/jest": "^26.0.21", 48 | "@types/react": "^17.0.2", 49 | "@types/react-native": "^0.67.0", 50 | "@types/react-test-renderer": "17.0.2", 51 | "babel-jest": "^26.6.3", 52 | "hybrid-navigation": "^2.15.1", 53 | "jest": "^26.6.3", 54 | "metro-react-native-babel-preset": "^0.66.2", 55 | "react": "17.0.2", 56 | "react-native": "0.67.4", 57 | "react-native-safe-area-context": "^4.5.0", 58 | "react-test-renderer": "17.0.2", 59 | "typescript": "^4.6.4" 60 | }, 61 | "jest": { 62 | "preset": "react-native", 63 | "moduleFileExtensions": [ 64 | "ts", 65 | "tsx", 66 | "js", 67 | "jsx", 68 | "json", 69 | "node" 70 | ] 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /react-native.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | project: { 3 | ios: { 4 | project: './example/ios/RNKeyboardInsets.xcworkspace', 5 | }, 6 | android: { 7 | sourceDir: './example/android/', 8 | }, 9 | }, 10 | } 11 | -------------------------------------------------------------------------------- /src/KeyboardInsetsView.tsx: -------------------------------------------------------------------------------- 1 | import React, { useCallback, useMemo, useRef } from 'react' 2 | import { Animated, NativeSyntheticEvent, ViewProps } from 'react-native' 3 | import { KeyboardStatus, NativeKeyboardInsetsView } from './native' 4 | 5 | export interface KeyboardState { 6 | height: number 7 | shown: boolean 8 | transitioning: boolean 9 | position: Animated.Value 10 | } 11 | 12 | const NativeKeyboardInsetsViewAnimated = Animated.createAnimatedComponent(NativeKeyboardInsetsView) 13 | 14 | interface KeyboardInsetsViewProps extends Animated.AnimatedProps { 15 | extraHeight?: number 16 | onKeyboard?: (status: KeyboardState) => void 17 | } 18 | 19 | export function KeyboardInsetsView(props: KeyboardInsetsViewProps) { 20 | const { children, onKeyboard, ...rest } = props 21 | 22 | const position = useRef(new Animated.Value(0)).current 23 | 24 | const onPositionChanged = useMemo( 25 | () => 26 | Animated.event( 27 | [ 28 | { 29 | nativeEvent: { 30 | position, 31 | }, 32 | }, 33 | ], 34 | { 35 | useNativeDriver: true, 36 | }, 37 | ), 38 | [position], 39 | ) 40 | 41 | const onStatusChanaged = useCallback( 42 | (event: NativeSyntheticEvent) => { 43 | onKeyboard?.({ ...event.nativeEvent, position }) 44 | }, 45 | [position, onKeyboard], 46 | ) 47 | 48 | if (onKeyboard) { 49 | return ( 50 | 55 | {children} 56 | 57 | ) 58 | } 59 | 60 | return {children} 61 | } 62 | -------------------------------------------------------------------------------- /src/hook.ts: -------------------------------------------------------------------------------- 1 | import { useCallback, useState } from 'react' 2 | import { Animated } from 'react-native' 3 | 4 | interface KeyboardState { 5 | height: number 6 | shown: boolean 7 | transitioning: boolean 8 | position: Animated.Value 9 | } 10 | 11 | export function useKeyboard() { 12 | const [keyboard, setKeyboard] = useState({ 13 | height: 0, 14 | shown: false, 15 | transitioning: false, 16 | position: new Animated.Value(0), 17 | }) 18 | 19 | const onKeyboard = useCallback((state: KeyboardState) => { 20 | setKeyboard(state) 21 | }, []) 22 | 23 | return { keyboard, onKeyboard } 24 | } 25 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import { KeyboardInsetsView, KeyboardState } from './KeyboardInsetsView' 2 | import { getEdgeInsetsForView } from './native' 3 | import { useKeyboard } from './hook' 4 | 5 | export { KeyboardInsetsView, KeyboardState, useKeyboard, getEdgeInsetsForView } 6 | -------------------------------------------------------------------------------- /src/native.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Insets, 3 | NativeModule, 4 | NativeModules, 5 | NativeSyntheticEvent, 6 | requireNativeComponent, 7 | ViewProps, 8 | Animated, 9 | } from 'react-native' 10 | 11 | export interface KeyboardStatus { 12 | height: number 13 | shown: boolean 14 | transitioning: boolean 15 | } 16 | 17 | interface KeyboardPosition { 18 | position: number 19 | } 20 | 21 | interface NativeKeyboardInsetsViewProps { 22 | mode?: 'auto' | 'manual' 23 | extraHeight?: number 24 | onStatusChanged?: (event: NativeSyntheticEvent) => void 25 | onPositionChanged?: (event: NativeSyntheticEvent) => void 26 | } 27 | 28 | export const NativeKeyboardInsetsView = requireNativeComponent< 29 | NativeKeyboardInsetsViewProps & Animated.AnimatedProps 30 | >('KeyboardInsetsView') 31 | 32 | interface KeyboardInsetsModuleInterface extends NativeModule { 33 | getEdgeInsetsForView(viewTag: number, callback: (insets: Insets) => void): void 34 | } 35 | 36 | const KeyboardInsetsModule: KeyboardInsetsModuleInterface = NativeModules.KeyboardInsetsModule 37 | 38 | export function getEdgeInsetsForView(viewTag: number, callback: (insets: Insets) => void) { 39 | KeyboardInsetsModule.getEdgeInsetsForView(viewTag, callback) 40 | } 41 | -------------------------------------------------------------------------------- /tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "noEmit": false 5 | }, 6 | "exclude": ["./example/**/*"] 7 | } 8 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "resolveJsonModule": true, 4 | "skipLibCheck": true, 5 | "target": "esnext", 6 | "module": "esnext", 7 | "lib": ["esnext"], 8 | "jsx": "react-native", 9 | "moduleResolution": "node", 10 | "declaration": true, 11 | "outDir": "./lib", 12 | "noEmit": true, 13 | "strict": true, 14 | "noImplicitAny": true, 15 | "baseUrl": "./", 16 | "paths": { 17 | "react-native-keyboard-insets": ["src/index"] 18 | }, 19 | "allowSyntheticDefaultImports": true, 20 | "esModuleInterop": true, 21 | "useUnknownInCatchVariables": false 22 | }, 23 | "include": ["./src/**/*", "./example/**/*"] 24 | } 25 | --------------------------------------------------------------------------------