├── .gitignore ├── README.md ├── android ├── build.gradle └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ ├── cn │ └── reactnative │ │ └── customkeyboard │ │ └── RNCustomKeyboardPackage.java │ └── com │ └── facebook │ └── react │ └── uimanager │ └── RNCustomKeyboardModule.java ├── index.js ├── ios ├── RNCustomKeyboard.h ├── RNCustomKeyboard.m └── RNCustomKeyboard.xcodeproj │ └── project.pbxproj └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | npm-debug* 3 | .idea 4 | node_modules 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # react-native-custom-keyboard 3 | 4 | ## Getting started 5 | 6 | `$ npm install react-native-custom-keyboard --save` 7 | 8 | ### Mostly automatic installation 9 | 10 | `$ react-native link react-native-custom-keyboard` 11 | 12 | ### Manual installation 13 | 14 | 15 | #### iOS 16 | 17 | 1. In XCode, in the project navigator, right click `Libraries` ➜ `Add Files to [your project's name]` 18 | 2. Go to `node_modules` ➜ `react-native-custom-keyboard` and add `RNCustomKeyboard.xcodeproj` 19 | 3. In XCode, in the project navigator, select your project. Add `libRNCustomKeyboard.a` to your project's `Build Phases` ➜ `Link Binary With Libraries` 20 | 4. Run your project (`Cmd+R`)< 21 | 22 | #### Android 23 | 24 | 1. Open up `android/app/src/main/java/[...]/MainActivity.java` 25 | - Add `import cn.reactnative.customkeyboard.RNCustomKeyboardPackage;` to the imports at the top of the file 26 | - Add `new RNCustomKeyboardPackage()` to the list returned by the `getPackages()` method 27 | 2. Append the following lines to `android/settings.gradle`: 28 | ``` 29 | include ':react-native-custom-keyboard' 30 | project(':react-native-custom-keyboard').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-custom-keyboard/android') 31 | ``` 32 | 3. Insert the following lines inside the dependencies block in `android/app/build.gradle`: 33 | ``` 34 | compile project(':react-native-custom-keyboard') 35 | ``` 36 | 37 | 38 | ## Usage 39 | 40 | Register a component as custom keyboard: 41 | 42 | ```javascript 43 | import React, { Component } from 'react'; 44 | import { 45 | TouchableOpacity, 46 | Text, 47 | View, 48 | } from 'react-native'; 49 | import { register, insertText } from 'react-native-custom-keyboard'; 50 | 51 | class MyKeyboard extends Component { 52 | onPress = () => { 53 | insertText(this.props.tag, 'Hello, world'); 54 | }; 55 | render() { 56 | return ( 57 | 58 | 59 | Hello, world 60 | 61 | 62 | ); 63 | } 64 | } 65 | 66 | register('hello', () => MyKeyboard); 67 | ``` 68 | 69 | Use `CustomTextInput` instead of `TextInput`. 70 | 71 | ```javascript 72 | import React, { Component } from 'react'; 73 | import {CustomTextInput} from 'react-native-custom-keyboard'; 74 | 75 | class MyPage extends Component { 76 | state = { 77 | value: ''; 78 | }; 79 | onChangeText = text => { 80 | this.setState({value: text}); 81 | }; 82 | render() { 83 | return ( 84 | 85 | 86 | 87 | ); 88 | } 89 | } 90 | ``` 91 | 92 | ## API 93 | 94 | ### register(type, type) 95 | 96 | Register a custom keyboard type. 97 | 98 | ### install(tag, type) 99 | 100 | Install custom keyboard to a `TextInput`. 101 | 102 | Generally you can use CustomTextInput instead of this. But you can use this API 103 | to install/change custom keyboard dynamically. 104 | 105 | ### uninstall(tag) 106 | 107 | Uninstall custom keyboard from a `TextInput` dynamically. 108 | 109 | ### insertText(tag, text) 110 | 111 | Use in a custom keyboard, insert text to `TextInput`. 112 | 113 | ### backSpace(tag) 114 | 115 | Use in a custom keyboard, delete selected text or the charactor before cursor. 116 | 117 | ### doDelete(tag) 118 | 119 | Use in a custom keyboard, delete selected text or the charactor after cursor. 120 | 121 | ### moveLeft(tag) 122 | 123 | Use in a custom keyboard, move cursor to selection start or move cursor left. 124 | 125 | ### moveRight(tag) 126 | 127 | Use in a custom keyboard, move cursor to selection end or move cursor right. 128 | 129 | ### switchSystemKeyboard(tag) 130 | 131 | Use in a custom keyboard. Switch to system keyboard. 132 | 133 | Next time user press or focus on the `TextInput`, custom keyboard will 134 | appear again. To keep using system keyboard, call `uninstall` instead. 135 | 136 | ### CustomTextInput 137 | 138 | Use instead of `TextInput`, this component support all properties of `TextInput`. 139 | 140 | #### prop: customKeyboardType: string 141 | 142 | Use a registered custom keyboard. 143 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | 2 | apply plugin: 'com.android.library' 3 | 4 | android { 5 | compileSdkVersion 23 6 | buildToolsVersion "23.0.1" 7 | 8 | defaultConfig { 9 | minSdkVersion 16 10 | targetSdkVersion 22 11 | versionCode 1 12 | versionName "1.0" 13 | ndk { 14 | abiFilters "armeabi-v7a", "x86" 15 | } 16 | } 17 | lintOptions { 18 | warning 'InvalidPackage' 19 | } 20 | } 21 | 22 | dependencies { 23 | compile 'com.facebook.react:react-native:+' 24 | } 25 | -------------------------------------------------------------------------------- /android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /android/src/main/java/cn/reactnative/customkeyboard/RNCustomKeyboardPackage.java: -------------------------------------------------------------------------------- 1 | 2 | package cn.reactnative.customkeyboard; 3 | 4 | import java.util.Arrays; 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.RNCustomKeyboardModule; 12 | import com.facebook.react.uimanager.ViewManager; 13 | import com.facebook.react.bridge.JavaScriptModule; 14 | public class RNCustomKeyboardPackage implements ReactPackage { 15 | @Override 16 | public List createNativeModules(ReactApplicationContext reactContext) { 17 | return Arrays.asList(new RNCustomKeyboardModule(reactContext)); 18 | } 19 | 20 | @Override 21 | public List> createJSModules() { 22 | return Collections.emptyList(); 23 | } 24 | 25 | @Override 26 | public List createViewManagers(ReactApplicationContext reactContext) { 27 | return Collections.emptyList(); 28 | } 29 | } -------------------------------------------------------------------------------- /android/src/main/java/com/facebook/react/uimanager/RNCustomKeyboardModule.java: -------------------------------------------------------------------------------- 1 | 2 | package com.facebook.react.uimanager; 3 | 4 | import android.app.Activity; 5 | import android.graphics.Color; 6 | import android.os.Bundle; 7 | import android.os.Handler; 8 | import android.os.Looper; 9 | import android.util.Log; 10 | import android.view.MotionEvent; 11 | import android.view.View; 12 | import android.view.ViewGroup; 13 | import android.view.inputmethod.InputMethodManager; 14 | import android.widget.EditText; 15 | import android.widget.RelativeLayout; 16 | 17 | import com.facebook.react.ReactApplication; 18 | import com.facebook.react.ReactRootView; 19 | import com.facebook.react.bridge.Arguments; 20 | import com.facebook.react.bridge.LifecycleEventListener; 21 | import com.facebook.react.bridge.Promise; 22 | import com.facebook.react.bridge.ReactApplicationContext; 23 | import com.facebook.react.bridge.ReactContextBaseJavaModule; 24 | import com.facebook.react.bridge.ReactMethod; 25 | import com.facebook.react.bridge.ReadableMap; 26 | import com.facebook.react.bridge.ReadableMapKeySetIterator; 27 | import com.facebook.react.bridge.UiThreadUtil; 28 | import com.facebook.react.bridge.WritableArray; 29 | import com.facebook.react.uimanager.RootView; 30 | import com.facebook.react.uimanager.UIManagerModule; 31 | import com.facebook.react.uimanager.UIViewOperationQueue; 32 | import com.facebook.react.views.textinput.ReactEditText; 33 | 34 | public class RNCustomKeyboardModule extends ReactContextBaseJavaModule { 35 | private final int TAG_ID = 0xdeadbeaf; 36 | private final ReactApplicationContext reactContext; 37 | 38 | public RNCustomKeyboardModule(ReactApplicationContext reactContext) { 39 | super(reactContext); 40 | this.reactContext = reactContext; 41 | } 42 | 43 | Handler handle = new Handler(Looper.getMainLooper()); 44 | 45 | private ReactEditText getEditById(int id) { 46 | UIViewOperationQueue uii = this.getReactApplicationContext().getNativeModule(UIManagerModule.class).getUIImplementation().getUIViewOperationQueue(); 47 | return (ReactEditText) uii.getNativeViewHierarchyManager().resolveView(id); 48 | } 49 | 50 | @ReactMethod 51 | public void install(final int tag, final String type) { 52 | UiThreadUtil.runOnUiThread(new Runnable() { 53 | @Override 54 | public void run() { 55 | final Activity activity = getCurrentActivity(); 56 | final ReactEditText edit = getEditById(tag); 57 | if (edit == null) { 58 | return; 59 | } 60 | 61 | edit.setTag(TAG_ID, createCustomKeyboard(activity, tag, type)); 62 | 63 | edit.setOnFocusChangeListener(new View.OnFocusChangeListener() { 64 | @Override 65 | public void onFocusChange(final View v, boolean hasFocus) { 66 | if (hasFocus) { 67 | View keyboard = (View)edit.getTag(TAG_ID); 68 | if (keyboard.getParent() == null) { 69 | activity.addContentView(keyboard, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); 70 | } 71 | UiThreadUtil.runOnUiThread(new Runnable() { 72 | @Override 73 | public void run() { 74 | ((InputMethodManager) getReactApplicationContext().getSystemService(Activity.INPUT_METHOD_SERVICE)).hideSoftInputFromWindow(v.getWindowToken(), 0); 75 | } 76 | }); 77 | } else { 78 | View keyboard = (View)edit.getTag(TAG_ID); 79 | if (keyboard.getParent() != null) { 80 | ((ViewGroup) keyboard.getParent()).removeView(keyboard); 81 | } 82 | } 83 | } 84 | }); 85 | 86 | edit.setOnClickListener(new View.OnClickListener(){ 87 | @Override 88 | public void onClick(final View v) { 89 | View keyboard = (View)edit.getTag(TAG_ID); 90 | if (keyboard.getParent() == null) { 91 | activity.addContentView(keyboard, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); 92 | } 93 | UiThreadUtil.runOnUiThread(new Runnable() { 94 | @Override 95 | public void run() { 96 | ((InputMethodManager) getReactApplicationContext().getSystemService(Activity.INPUT_METHOD_SERVICE)).hideSoftInputFromWindow(v.getWindowToken(), 0); 97 | } 98 | }); 99 | } 100 | }); 101 | } 102 | }); 103 | } 104 | 105 | ReactRootView rootView = null; 106 | 107 | private View createCustomKeyboard(Activity activity, int tag, String type) { 108 | RelativeLayout layout = new RelativeLayout(activity); 109 | rootView = new ReactRootView(this.getReactApplicationContext()); 110 | rootView.setBackgroundColor(Color.WHITE); 111 | 112 | Bundle bundle = new Bundle(); 113 | bundle.putInt("tag", tag); 114 | bundle.putString("type", type); 115 | rootView.startReactApplication( 116 | ((ReactApplication) activity.getApplication()).getReactNativeHost().getReactInstanceManager(), 117 | "CustomKeyboard", 118 | bundle); 119 | 120 | final float scale = activity.getResources().getDisplayMetrics().density; 121 | RelativeLayout.LayoutParams lParams = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, Math.round(216*scale)); 122 | lParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM, RelativeLayout.TRUE); 123 | layout.addView(rootView, lParams); 124 | // activity.addContentView(layout, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); 125 | return layout; 126 | } 127 | 128 | @ReactMethod 129 | public void uninstall(final int tag) { 130 | UiThreadUtil.runOnUiThread(new Runnable() { 131 | @Override 132 | public void run() { 133 | final Activity activity = getCurrentActivity(); 134 | final ReactEditText edit = getEditById(tag); 135 | if (edit == null) { 136 | return; 137 | } 138 | 139 | edit.setTag(TAG_ID, null); 140 | } 141 | }); 142 | } 143 | 144 | @ReactMethod 145 | public void insertText(final int tag, final String text) { 146 | UiThreadUtil.runOnUiThread(new Runnable() { 147 | @Override 148 | public void run() { 149 | final Activity activity = getCurrentActivity(); 150 | final ReactEditText edit = getEditById(tag); 151 | if (edit == null) { 152 | return; 153 | } 154 | 155 | int start = Math.max(edit.getSelectionStart(), 0); 156 | int end = Math.max(edit.getSelectionEnd(), 0); 157 | edit.getText().replace(Math.min(start, end), Math.max(start, end), 158 | text, 0, text.length()); 159 | } 160 | }); 161 | } 162 | 163 | @ReactMethod 164 | public void backSpace(final int tag) { 165 | UiThreadUtil.runOnUiThread(new Runnable() { 166 | @Override 167 | public void run() { 168 | final Activity activity = getCurrentActivity(); 169 | final ReactEditText edit = getEditById(tag); 170 | if (edit == null) { 171 | return; 172 | } 173 | 174 | int start = Math.max(edit.getSelectionStart(), 0); 175 | int end = Math.max(edit.getSelectionEnd(), 0); 176 | if (start != end) { 177 | edit.getText().delete(start, end); 178 | } else if (start > 0){ 179 | edit.getText().delete(start - 1, end); 180 | } 181 | } 182 | }); 183 | } 184 | 185 | @ReactMethod 186 | public void doDelete(final int tag) { 187 | UiThreadUtil.runOnUiThread(new Runnable() { 188 | @Override 189 | public void run() { 190 | final Activity activity = getCurrentActivity(); 191 | final ReactEditText edit = getEditById(tag); 192 | if (edit == null) { 193 | return; 194 | } 195 | 196 | int start = Math.max(edit.getSelectionStart(), 0); 197 | int end = Math.max(edit.getSelectionEnd(), 0); 198 | if (start != end) { 199 | edit.getText().delete(start, end); 200 | } else if (start > 0){ 201 | edit.getText().delete(start, end+1); 202 | } 203 | } 204 | }); 205 | } 206 | 207 | @ReactMethod 208 | public void moveLeft(final int tag) { 209 | UiThreadUtil.runOnUiThread(new Runnable() { 210 | @Override 211 | public void run() { 212 | final Activity activity = getCurrentActivity(); 213 | final ReactEditText edit = getEditById(tag); 214 | if (edit == null) { 215 | return; 216 | } 217 | 218 | int start = Math.max(edit.getSelectionStart(), 0); 219 | int end = Math.max(edit.getSelectionEnd(), 0); 220 | if (start != end) { 221 | edit.setSelection(start, start); 222 | } else { 223 | edit.setSelection(start - 1, start - 1); 224 | } 225 | } 226 | }); 227 | } 228 | 229 | @ReactMethod 230 | public void moveRight(final int tag) { 231 | UiThreadUtil.runOnUiThread(new Runnable() { 232 | @Override 233 | public void run() { 234 | final Activity activity = getCurrentActivity(); 235 | final ReactEditText edit = getEditById(tag); 236 | if (edit == null) { 237 | return; 238 | } 239 | 240 | int start = Math.max(edit.getSelectionStart(), 0); 241 | int end = Math.max(edit.getSelectionEnd(), 0); 242 | if (start != end) { 243 | edit.setSelection(end, end); 244 | } else if (start > 0){ 245 | edit.setSelection(end + 1, end + 1); 246 | } 247 | } 248 | }); 249 | } 250 | 251 | @ReactMethod 252 | public void switchSystemKeyboard(final int tag) { 253 | UiThreadUtil.runOnUiThread(new Runnable() { 254 | @Override 255 | public void run() { 256 | final Activity activity = getCurrentActivity(); 257 | final ReactEditText edit = getEditById(tag); 258 | if (edit == null) { 259 | return; 260 | } 261 | 262 | View keyboard = (View)edit.getTag(TAG_ID); 263 | if (keyboard.getParent() != null) { 264 | ((ViewGroup) keyboard.getParent()).removeView(keyboard); 265 | } 266 | UiThreadUtil.runOnUiThread(new Runnable() { 267 | 268 | @Override 269 | public void run() { 270 | ((InputMethodManager) getReactApplicationContext().getSystemService(Activity.INPUT_METHOD_SERVICE)).showSoftInput(edit, InputMethodManager.SHOW_IMPLICIT); 271 | } 272 | }); 273 | } 274 | }); 275 | } 276 | 277 | @Override 278 | public String getName() { 279 | return "CustomKeyboard"; 280 | } 281 | } -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 2 | import React, { Component, PropTypes } from 'react'; 3 | 4 | import { 5 | NativeModules, 6 | TextInput, 7 | findNodeHandle, 8 | AppRegistry, 9 | } from 'react-native'; 10 | 11 | const { CustomKeyboard} = NativeModules; 12 | 13 | const { 14 | install, uninstall, 15 | insertText, backSpace, doDelete, 16 | moveLeft, moveRight, 17 | switchSystemKeyboard, 18 | } = CustomKeyboard; 19 | 20 | export { 21 | install, uninstall, 22 | insertText, backSpace, doDelete, 23 | moveLeft, moveRight, 24 | switchSystemKeyboard, 25 | }; 26 | 27 | const keyboardTypeRegistry = {}; 28 | 29 | export function register(type, factory) { 30 | keyboardTypeRegistry[type] = factory; 31 | } 32 | 33 | class CustomKeyboardContainer extends Component { 34 | render() { 35 | const {tag, type} = this.props; 36 | const factory = keyboardTypeRegistry[type]; 37 | if (!factory) { 38 | console.warn(`Custom keyboard type ${type} not registered.`); 39 | return null; 40 | } 41 | const Comp = factory(); 42 | return ; 43 | } 44 | } 45 | 46 | AppRegistry.registerComponent("CustomKeyboard", ()=>CustomKeyboardContainer); 47 | 48 | export class CustomTextInput extends Component { 49 | static propTypes = { 50 | ...TextInput.propTypes, 51 | customKeyboardType: PropTypes.string, 52 | }; 53 | componentDidMount() { 54 | install(findNodeHandle(this.input), this.props.customKeyboardType); 55 | } 56 | componentWillReceiveProps(newProps) { 57 | if (newProps.customKeyboardType !== this.props.customKeyboardType) { 58 | install(findNodeHandle(this.input), newProps.customKeyboardType); 59 | } 60 | } 61 | onRef = ref => { 62 | this.input = ref; 63 | }; 64 | render() { 65 | const { customKeyboardType, ...others } = this.props; 66 | return ; 67 | } 68 | } -------------------------------------------------------------------------------- /ios/RNCustomKeyboard.h: -------------------------------------------------------------------------------- 1 | 2 | #import "RCTBridgeModule.h" 3 | 4 | @interface RNCustomKeyboard : NSObject 5 | 6 | @end 7 | -------------------------------------------------------------------------------- /ios/RNCustomKeyboard.m: -------------------------------------------------------------------------------- 1 | 2 | #import "RNCustomKeyboard.h" 3 | #import "RCTBridge+Private.h" 4 | #import "RCTUIManager.h" 5 | 6 | @implementation RNCustomKeyboard 7 | 8 | @synthesize bridge = _bridge; 9 | 10 | - (dispatch_queue_t)methodQueue 11 | { 12 | return dispatch_get_main_queue(); 13 | } 14 | RCT_EXPORT_MODULE(CustomKeyboard) 15 | 16 | RCT_EXPORT_METHOD(install:(nonnull NSNumber *)reactTag withType:(nonnull NSString *)keyboardType) 17 | { 18 | UIView* inputView = [[RCTRootView alloc] initWithBridge:((RCTBatchedBridge *)_bridge).parentBridge moduleName:@"CustomKeyboard" initialProperties: 19 | @{ 20 | @"tag": reactTag, 21 | @"type": keyboardType 22 | } 23 | ]; 24 | 25 | UITextView *view = (UITextView*)[_bridge.uiManager viewForReactTag:reactTag]; 26 | 27 | view.inputView = inputView; 28 | [view reloadInputViews]; 29 | } 30 | 31 | RCT_EXPORT_METHOD(uninstall:(nonnull NSNumber *)reactTag) 32 | { 33 | UITextView *view = (UITextView*)[_bridge.uiManager viewForReactTag:reactTag]; 34 | 35 | view.inputView = nil; 36 | [view reloadInputViews]; 37 | } 38 | 39 | RCT_EXPORT_METHOD(insertText:(nonnull NSNumber *)reactTag withText:(NSString*)text) { 40 | UITextView *view = (UITextView*)[_bridge.uiManager viewForReactTag:reactTag]; 41 | 42 | [view replaceRange:view.selectedTextRange withText:text]; 43 | } 44 | 45 | RCT_EXPORT_METHOD(backSpace:(nonnull NSNumber *)reactTag) { 46 | UITextView *view = (UITextView*)[_bridge.uiManager viewForReactTag:reactTag]; 47 | 48 | UITextRange* range = view.selectedTextRange; 49 | if ([view comparePosition:range.start toPosition:range.end] == 0) { 50 | range = [view textRangeFromPosition:[view positionFromPosition:range.start offset:-1] toPosition:range.start]; 51 | } 52 | [view replaceRange:range withText:@""]; 53 | } 54 | 55 | RCT_EXPORT_METHOD(doDelete:(nonnull NSNumber *)reactTag) { 56 | UITextView *view = (UITextView*)[_bridge.uiManager viewForReactTag:reactTag]; 57 | 58 | UITextRange* range = view.selectedTextRange; 59 | if ([view comparePosition:range.start toPosition:range.end] == 0) { 60 | range = [view textRangeFromPosition:range.start toPosition:[view positionFromPosition: range.start offset: 1]]; 61 | } 62 | [view replaceRange:range withText:@""]; 63 | } 64 | 65 | RCT_EXPORT_METHOD(moveLeft:(nonnull NSNumber *)reactTag) { 66 | UITextView *view = (UITextView*)[_bridge.uiManager viewForReactTag:reactTag]; 67 | 68 | UITextRange* range = view.selectedTextRange; 69 | UITextPosition* position = range.start; 70 | 71 | if ([view comparePosition:range.start toPosition:range.end] == 0) { 72 | position = [view positionFromPosition: position, offset: -1]; 73 | } 74 | 75 | view.selectedTextRange = [view textRangeFromPosition: position toPosition:position]; 76 | } 77 | 78 | RCT_EXPORT_METHOD(moveRight:(nonnull NSNumber *)reactTag) { 79 | UITextView *view = (UITextView*)[_bridge.uiManager viewForReactTag:reactTag]; 80 | 81 | UITextRange* range = view.selectedTextRange; 82 | UITextPosition* position = range.end; 83 | 84 | if ([view comparePosition:range.start toPosition:range.end] == 0) { 85 | position = [view positionFromPosition: position, offset: 1]; 86 | } 87 | 88 | view.selectedTextRange = [view textRangeFromPosition: position toPosition:position]; 89 | } 90 | 91 | RCT_EXPORT_METHOD(switchSystemKeyboard:(nonnull NSNumber*) reactTag) { 92 | UITextView *view = [_bridge.uiManager viewForReactTag:reactTag]; 93 | UIView* inputView = view.inputView; 94 | view.inputView = nil; 95 | [view reloadInputViews]; 96 | view.inputView = inputView; 97 | } 98 | 99 | @end 100 | -------------------------------------------------------------------------------- /ios/RNCustomKeyboard.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | B3E7B58A1CC2AC0600A0062D /* RNCustomKeyboard.m in Sources */ = {isa = PBXBuildFile; fileRef = B3E7B5891CC2AC0600A0062D /* RNCustomKeyboard.m */; }; 11 | /* End PBXBuildFile section */ 12 | 13 | /* Begin PBXCopyFilesBuildPhase section */ 14 | 58B511D91A9E6C8500147676 /* 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 | 134814201AA4EA6300B7C361 /* libRNCustomKeyboard.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRNCustomKeyboard.a; sourceTree = BUILT_PRODUCTS_DIR; }; 27 | B3E7B5881CC2AC0600A0062D /* RNCustomKeyboard.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNCustomKeyboard.h; sourceTree = ""; }; 28 | B3E7B5891CC2AC0600A0062D /* RNCustomKeyboard.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNCustomKeyboard.m; sourceTree = ""; }; 29 | /* End PBXFileReference section */ 30 | 31 | /* Begin PBXFrameworksBuildPhase section */ 32 | 58B511D81A9E6C8500147676 /* Frameworks */ = { 33 | isa = PBXFrameworksBuildPhase; 34 | buildActionMask = 2147483647; 35 | files = ( 36 | ); 37 | runOnlyForDeploymentPostprocessing = 0; 38 | }; 39 | /* End PBXFrameworksBuildPhase section */ 40 | 41 | /* Begin PBXGroup section */ 42 | 134814211AA4EA7D00B7C361 /* Products */ = { 43 | isa = PBXGroup; 44 | children = ( 45 | 134814201AA4EA6300B7C361 /* libRNCustomKeyboard.a */, 46 | ); 47 | name = Products; 48 | sourceTree = ""; 49 | }; 50 | 58B511D21A9E6C8500147676 = { 51 | isa = PBXGroup; 52 | children = ( 53 | B3E7B5881CC2AC0600A0062D /* RNCustomKeyboard.h */, 54 | B3E7B5891CC2AC0600A0062D /* RNCustomKeyboard.m */, 55 | 134814211AA4EA7D00B7C361 /* Products */, 56 | ); 57 | sourceTree = ""; 58 | }; 59 | /* End PBXGroup section */ 60 | 61 | /* Begin PBXNativeTarget section */ 62 | 58B511DA1A9E6C8500147676 /* RNCustomKeyboard */ = { 63 | isa = PBXNativeTarget; 64 | buildConfigurationList = 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "RNCustomKeyboard" */; 65 | buildPhases = ( 66 | 58B511D71A9E6C8500147676 /* Sources */, 67 | 58B511D81A9E6C8500147676 /* Frameworks */, 68 | 58B511D91A9E6C8500147676 /* CopyFiles */, 69 | ); 70 | buildRules = ( 71 | ); 72 | dependencies = ( 73 | ); 74 | name = RNCustomKeyboard; 75 | productName = RCTDataManager; 76 | productReference = 134814201AA4EA6300B7C361 /* libRNCustomKeyboard.a */; 77 | productType = "com.apple.product-type.library.static"; 78 | }; 79 | /* End PBXNativeTarget section */ 80 | 81 | /* Begin PBXProject section */ 82 | 58B511D31A9E6C8500147676 /* Project object */ = { 83 | isa = PBXProject; 84 | attributes = { 85 | LastUpgradeCheck = 0610; 86 | ORGANIZATIONNAME = Facebook; 87 | TargetAttributes = { 88 | 58B511DA1A9E6C8500147676 = { 89 | CreatedOnToolsVersion = 6.1.1; 90 | }; 91 | }; 92 | }; 93 | buildConfigurationList = 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "RNCustomKeyboard" */; 94 | compatibilityVersion = "Xcode 3.2"; 95 | developmentRegion = English; 96 | hasScannedForEncodings = 0; 97 | knownRegions = ( 98 | en, 99 | ); 100 | mainGroup = 58B511D21A9E6C8500147676; 101 | productRefGroup = 58B511D21A9E6C8500147676; 102 | projectDirPath = ""; 103 | projectRoot = ""; 104 | targets = ( 105 | 58B511DA1A9E6C8500147676 /* RNCustomKeyboard */, 106 | ); 107 | }; 108 | /* End PBXProject section */ 109 | 110 | /* Begin PBXSourcesBuildPhase section */ 111 | 58B511D71A9E6C8500147676 /* Sources */ = { 112 | isa = PBXSourcesBuildPhase; 113 | buildActionMask = 2147483647; 114 | files = ( 115 | B3E7B58A1CC2AC0600A0062D /* RNCustomKeyboard.m in Sources */, 116 | ); 117 | runOnlyForDeploymentPostprocessing = 0; 118 | }; 119 | /* End PBXSourcesBuildPhase section */ 120 | 121 | /* Begin XCBuildConfiguration section */ 122 | 58B511ED1A9E6C8500147676 /* Debug */ = { 123 | isa = XCBuildConfiguration; 124 | buildSettings = { 125 | ALWAYS_SEARCH_USER_PATHS = NO; 126 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 127 | CLANG_CXX_LIBRARY = "libc++"; 128 | CLANG_ENABLE_MODULES = YES; 129 | CLANG_ENABLE_OBJC_ARC = YES; 130 | CLANG_WARN_BOOL_CONVERSION = YES; 131 | CLANG_WARN_CONSTANT_CONVERSION = YES; 132 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 133 | CLANG_WARN_EMPTY_BODY = YES; 134 | CLANG_WARN_ENUM_CONVERSION = YES; 135 | CLANG_WARN_INT_CONVERSION = YES; 136 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 137 | CLANG_WARN_UNREACHABLE_CODE = YES; 138 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 139 | COPY_PHASE_STRIP = NO; 140 | ENABLE_STRICT_OBJC_MSGSEND = YES; 141 | GCC_C_LANGUAGE_STANDARD = gnu99; 142 | GCC_DYNAMIC_NO_PIC = NO; 143 | GCC_OPTIMIZATION_LEVEL = 0; 144 | GCC_PREPROCESSOR_DEFINITIONS = ( 145 | "DEBUG=1", 146 | "$(inherited)", 147 | ); 148 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 149 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 150 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 151 | GCC_WARN_UNDECLARED_SELECTOR = YES; 152 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 153 | GCC_WARN_UNUSED_FUNCTION = YES; 154 | GCC_WARN_UNUSED_VARIABLE = YES; 155 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 156 | MTL_ENABLE_DEBUG_INFO = YES; 157 | ONLY_ACTIVE_ARCH = YES; 158 | SDKROOT = iphoneos; 159 | }; 160 | name = Debug; 161 | }; 162 | 58B511EE1A9E6C8500147676 /* Release */ = { 163 | isa = XCBuildConfiguration; 164 | buildSettings = { 165 | ALWAYS_SEARCH_USER_PATHS = NO; 166 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 167 | CLANG_CXX_LIBRARY = "libc++"; 168 | CLANG_ENABLE_MODULES = YES; 169 | CLANG_ENABLE_OBJC_ARC = YES; 170 | CLANG_WARN_BOOL_CONVERSION = YES; 171 | CLANG_WARN_CONSTANT_CONVERSION = YES; 172 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 173 | CLANG_WARN_EMPTY_BODY = YES; 174 | CLANG_WARN_ENUM_CONVERSION = YES; 175 | CLANG_WARN_INT_CONVERSION = YES; 176 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 177 | CLANG_WARN_UNREACHABLE_CODE = YES; 178 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 179 | COPY_PHASE_STRIP = YES; 180 | ENABLE_NS_ASSERTIONS = NO; 181 | ENABLE_STRICT_OBJC_MSGSEND = YES; 182 | GCC_C_LANGUAGE_STANDARD = gnu99; 183 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 184 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 185 | GCC_WARN_UNDECLARED_SELECTOR = YES; 186 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 187 | GCC_WARN_UNUSED_FUNCTION = YES; 188 | GCC_WARN_UNUSED_VARIABLE = YES; 189 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 190 | MTL_ENABLE_DEBUG_INFO = NO; 191 | SDKROOT = iphoneos; 192 | VALIDATE_PRODUCT = YES; 193 | }; 194 | name = Release; 195 | }; 196 | 58B511F01A9E6C8500147676 /* Debug */ = { 197 | isa = XCBuildConfiguration; 198 | buildSettings = { 199 | HEADER_SEARCH_PATHS = ( 200 | "$(inherited)", 201 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, 202 | "$(SRCROOT)/../../../React/**", 203 | "$(SRCROOT)/../../react-native/React/**", 204 | ); 205 | LIBRARY_SEARCH_PATHS = "$(inherited)"; 206 | OTHER_LDFLAGS = "-ObjC"; 207 | PRODUCT_NAME = RNCustomKeyboard; 208 | SKIP_INSTALL = YES; 209 | }; 210 | name = Debug; 211 | }; 212 | 58B511F11A9E6C8500147676 /* Release */ = { 213 | isa = XCBuildConfiguration; 214 | buildSettings = { 215 | HEADER_SEARCH_PATHS = ( 216 | "$(inherited)", 217 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, 218 | "$(SRCROOT)/../../../React/**", 219 | "$(SRCROOT)/../../react-native/React/**", 220 | ); 221 | LIBRARY_SEARCH_PATHS = "$(inherited)"; 222 | OTHER_LDFLAGS = "-ObjC"; 223 | PRODUCT_NAME = RNCustomKeyboard; 224 | SKIP_INSTALL = YES; 225 | }; 226 | name = Release; 227 | }; 228 | /* End XCBuildConfiguration section */ 229 | 230 | /* Begin XCConfigurationList section */ 231 | 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "RNCustomKeyboard" */ = { 232 | isa = XCConfigurationList; 233 | buildConfigurations = ( 234 | 58B511ED1A9E6C8500147676 /* Debug */, 235 | 58B511EE1A9E6C8500147676 /* Release */, 236 | ); 237 | defaultConfigurationIsVisible = 0; 238 | defaultConfigurationName = Release; 239 | }; 240 | 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "RNCustomKeyboard" */ = { 241 | isa = XCConfigurationList; 242 | buildConfigurations = ( 243 | 58B511F01A9E6C8500147676 /* Debug */, 244 | 58B511F11A9E6C8500147676 /* Release */, 245 | ); 246 | defaultConfigurationIsVisible = 0; 247 | defaultConfigurationName = Release; 248 | }; 249 | /* End XCConfigurationList section */ 250 | }; 251 | rootObject = 58B511D31A9E6C8500147676 /* Project object */; 252 | } 253 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | 2 | { 3 | "name": "react-native-custom-keyboard", 4 | "version": "1.0.3", 5 | "description": "", 6 | "main": "index.js", 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/reactnativecn/react-native-custom-keyboard.git" 13 | }, 14 | "keywords": [ 15 | "react-native" 16 | ], 17 | "author": "", 18 | "license": "", 19 | "peerDependencies": { 20 | "react-native": "^0.29.0" 21 | } 22 | } 23 | --------------------------------------------------------------------------------