diff --git a/Libraries/Components/TextInput/InputAccessoryView.js b/Libraries/Components/TextInput/InputAccessoryView.js
new file mode 100644
index 00000000000000..cd12758435ff96
--- /dev/null
+++ b/Libraries/Components/TextInput/InputAccessoryView.js
@@ -0,0 +1,114 @@
+/**
+ * Copyright (c) 2013-present, Facebook, Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @providesModule InputAccessoryView
+ * @flow
+ * @format
+ */
+'use strict';
+
+const ColorPropType = require('ColorPropType');
+const React = require('React');
+const StyleSheet = require('StyleSheet');
+const ViewPropTypes = require('ViewPropTypes');
+
+const requireNativeComponent = require('requireNativeComponent');
+
+const RCTInputAccessoryView = requireNativeComponent('RCTInputAccessoryView');
+
+/**
+ * Note: iOS only
+ *
+ * A component which enables customization of the keyboard input accessory view.
+ * The input accessory view is displayed above the keyboard whenever a TextInput
+ * has focus. This component can be used to create custom toolbars.
+ *
+ * To use this component wrap your custom toolbar with the
+ * InputAccessoryView component, and set a nativeID. Then, pass that nativeID
+ * as the inputAccessoryViewID of whatever TextInput you desire. A simple
+ * example:
+ *
+ * ```ReactNativeWebPlayer
+ * import React, { Component } from 'react';
+ * import { AppRegistry, TextInput, InputAccessoryView, Button } from 'react-native';
+ *
+ * export default class UselessTextInput extends Component {
+ * constructor(props) {
+ * super(props);
+ * this.state = {text: 'Placeholder Text'};
+ * }
+ *
+ * render() {
+ * const inputAccessoryViewID = "uniqueID";
+ * return (
+ *
+ *
+ * this.setState({text})}
+ * value={this.state.text}
+ * />
+ *
+ *
+ *
+ *
+ * );
+ * }
+ * }
+ *
+ * // skip this line if using Create React Native App
+ * AppRegistry.registerComponent('AwesomeProject', () => UselessTextInput);
+ * ```
+ *
+ * This component can also be used to create sticky text inputs (text inputs
+ * which are anchored to the top of the keyboard). To do this, wrap a
+ * TextInput with the InputAccessoryView component, and don't set a nativeID.
+ * For an example, look at InputAccessoryViewExample.js in RNTester.
+ */
+
+type Props = {
+ +children: React.Node,
+ /**
+ * An ID which is used to associate this `InputAccessoryView` to
+ * specified TextInput(s).
+ */
+ nativeID?: string,
+ style?: ViewPropTypes.style,
+ backgroundColor?: ColorPropType,
+};
+
+class InputAccessoryView extends React.Component {
+ render(): React.Node {
+ if (React.Children.count(this.props.children) === 0) {
+ return null;
+ }
+
+ return (
+
+ {this.props.children}
+
+ );
+ }
+}
+
+const styles = StyleSheet.create({
+ container: {
+ position: 'absolute',
+ },
+});
+
+module.exports = InputAccessoryView;
diff --git a/Libraries/Components/TextInput/TextInput.js b/Libraries/Components/TextInput/TextInput.js
index 3ce82932e17b33..57dc866b9dc44a 100644
--- a/Libraries/Components/TextInput/TextInput.js
+++ b/Libraries/Components/TextInput/TextInput.js
@@ -590,6 +590,13 @@ const TextInput = createReactClass({
* This property is supported only for single-line TextInput component on iOS.
*/
caretHidden: PropTypes.bool,
+ /**
+ * An optional identifier which links a custom InputAccessoryView to
+ * this text input. The InputAccessoryView is rendered above the
+ * keyboard when this text input is focused.
+ * @platform ios
+ */
+ inputAccessoryViewID: PropTypes.string,
},
getDefaultProps(): Object {
return {
diff --git a/Libraries/Text/RCTText.xcodeproj/project.pbxproj b/Libraries/Text/RCTText.xcodeproj/project.pbxproj
index a79fd942326734..da27ece2a1b138 100644
--- a/Libraries/Text/RCTText.xcodeproj/project.pbxproj
+++ b/Libraries/Text/RCTText.xcodeproj/project.pbxproj
@@ -103,6 +103,9 @@
5956B1A6200FF35C008D9D16 /* RCTUITextField.m in Sources */ = {isa = PBXBuildFile; fileRef = 5956B103200FEBA9008D9D16 /* RCTUITextField.m */; };
5956B1A7200FF35C008D9D16 /* RCTVirtualTextShadowView.m in Sources */ = {isa = PBXBuildFile; fileRef = 5956B12E200FEBAA008D9D16 /* RCTVirtualTextShadowView.m */; };
5956B1A8200FF35C008D9D16 /* RCTVirtualTextViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 5956B12B200FEBAA008D9D16 /* RCTVirtualTextViewManager.m */; };
+ 8F2807C7202D2B6B005D65E6 /* RCTInputAccessoryViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 8F2807C1202D2B6A005D65E6 /* RCTInputAccessoryViewManager.m */; };
+ 8F2807C8202D2B6B005D65E6 /* RCTInputAccessoryView.m in Sources */ = {isa = PBXBuildFile; fileRef = 8F2807C3202D2B6A005D65E6 /* RCTInputAccessoryView.m */; };
+ 8F2807C9202D2B6B005D65E6 /* RCTInputAccessoryViewContent.m in Sources */ = {isa = PBXBuildFile; fileRef = 8F2807C5202D2B6B005D65E6 /* RCTInputAccessoryViewContent.m */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
@@ -229,6 +232,12 @@
5956B12D200FEBAA008D9D16 /* RCTVirtualTextViewManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTVirtualTextViewManager.h; sourceTree = ""; };
5956B12E200FEBAA008D9D16 /* RCTVirtualTextShadowView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTVirtualTextShadowView.m; sourceTree = ""; };
5956B12F200FEBAA008D9D16 /* RCTConvert+Text.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "RCTConvert+Text.m"; sourceTree = ""; };
+ 8F2807C1202D2B6A005D65E6 /* RCTInputAccessoryViewManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTInputAccessoryViewManager.m; sourceTree = ""; };
+ 8F2807C2202D2B6A005D65E6 /* RCTInputAccessoryViewContent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTInputAccessoryViewContent.h; sourceTree = ""; };
+ 8F2807C3202D2B6A005D65E6 /* RCTInputAccessoryView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTInputAccessoryView.m; sourceTree = ""; };
+ 8F2807C4202D2B6A005D65E6 /* RCTInputAccessoryView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTInputAccessoryView.h; sourceTree = ""; };
+ 8F2807C5202D2B6B005D65E6 /* RCTInputAccessoryViewContent.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTInputAccessoryViewContent.m; sourceTree = ""; };
+ 8F2807C6202D2B6B005D65E6 /* RCTInputAccessoryViewManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTInputAccessoryViewManager.h; sourceTree = ""; };
/* End PBXFileReference section */
/* Begin PBXGroup section */
@@ -274,6 +283,12 @@
5956B0FF200FEBA9008D9D16 /* TextInput */ = {
isa = PBXGroup;
children = (
+ 8F2807C4202D2B6A005D65E6 /* RCTInputAccessoryView.h */,
+ 8F2807C3202D2B6A005D65E6 /* RCTInputAccessoryView.m */,
+ 8F2807C2202D2B6A005D65E6 /* RCTInputAccessoryViewContent.h */,
+ 8F2807C5202D2B6B005D65E6 /* RCTInputAccessoryViewContent.m */,
+ 8F2807C6202D2B6B005D65E6 /* RCTInputAccessoryViewManager.h */,
+ 8F2807C1202D2B6A005D65E6 /* RCTInputAccessoryViewManager.m */,
5956B113200FEBA9008D9D16 /* Multiline */,
5956B10C200FEBA9008D9D16 /* RCTBackedTextInputDelegate.h */,
5956B107200FEBA9008D9D16 /* RCTBackedTextInputDelegateAdapter.h */,
@@ -465,8 +480,11 @@
5956B140200FEBAA008D9D16 /* RCTTextShadowView.m in Sources */,
5956B131200FEBAA008D9D16 /* RCTRawTextViewManager.m in Sources */,
5956B137200FEBAA008D9D16 /* RCTBaseTextInputShadowView.m in Sources */,
+ 8F2807C7202D2B6B005D65E6 /* RCTInputAccessoryViewManager.m in Sources */,
5956B146200FEBAA008D9D16 /* RCTConvert+Text.m in Sources */,
+ 8F2807C9202D2B6B005D65E6 /* RCTInputAccessoryViewContent.m in Sources */,
5956B13F200FEBAA008D9D16 /* RCTTextAttributes.m in Sources */,
+ 8F2807C8202D2B6B005D65E6 /* RCTInputAccessoryView.m in Sources */,
5956B143200FEBAA008D9D16 /* RCTTextView.m in Sources */,
5956B13C200FEBAA008D9D16 /* RCTUITextView.m in Sources */,
5956B136200FEBAA008D9D16 /* RCTBackedTextInputDelegateAdapter.m in Sources */,
diff --git a/Libraries/Text/TextInput/RCTBaseTextInputView.h b/Libraries/Text/TextInput/RCTBaseTextInputView.h
index fe86b334ba8a3a..6de343f2fa06d2 100644
--- a/Libraries/Text/TextInput/RCTBaseTextInputView.h
+++ b/Libraries/Text/TextInput/RCTBaseTextInputView.h
@@ -46,6 +46,7 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic, copy) RCTTextSelection *selection;
@property (nonatomic, strong, nullable) NSNumber *maxLength;
@property (nonatomic, copy) NSAttributedString *attributedText;
+@property (nonatomic, copy) NSString *inputAccessoryViewID;
@end
diff --git a/Libraries/Text/TextInput/RCTBaseTextInputView.m b/Libraries/Text/TextInput/RCTBaseTextInputView.m
index 2d8bb0035fbdd5..025fb0e4168c8f 100644
--- a/Libraries/Text/TextInput/RCTBaseTextInputView.m
+++ b/Libraries/Text/TextInput/RCTBaseTextInputView.m
@@ -15,6 +15,8 @@
#import
#import
+#import "RCTInputAccessoryView.h"
+#import "RCTInputAccessoryViewContent.h"
#import "RCTTextAttributes.h"
#import "RCTTextSelection.h"
@@ -400,12 +402,33 @@ - (void)didMoveToWindow
- (void)didSetProps:(NSArray *)changedProps
{
- [self invalidateInputAccessoryView];
+ #if !TARGET_OS_TV
+ if ([changedProps containsObject:@"inputAccessoryViewID"] && self.inputAccessoryViewID) {
+ [self setCustomInputAccessoryViewWithNativeID:self.inputAccessoryViewID];
+ } else if (!self.inputAccessoryViewID) {
+ [self setDefaultInputAccessoryView];
+ }
+ #endif
+}
+
+- (void)setCustomInputAccessoryViewWithNativeID:(NSString *)nativeID
+{
+ __weak RCTBaseTextInputView *weakSelf = self;
+ [_bridge.uiManager rootViewForReactTag:self.reactTag withCompletion:^(UIView *rootView) {
+ RCTBaseTextInputView *strongSelf = weakSelf;
+ if (rootView) {
+ UIView *accessoryView = [strongSelf->_bridge.uiManager viewForNativeID:nativeID
+ withRootTag:rootView.reactTag];
+ if (accessoryView && [accessoryView isKindOfClass:[RCTInputAccessoryView class]]) {
+ strongSelf.backedTextInputView.inputAccessoryView = ((RCTInputAccessoryView *)accessoryView).content.inputAccessoryView;
+ [strongSelf reloadInputViewsIfNecessary];
+ }
+ }
+ }];
}
-- (void)invalidateInputAccessoryView
+- (void)setDefaultInputAccessoryView
{
-#if !TARGET_OS_TV
UIView *textInputView = self.backedTextInputView;
UIKeyboardType keyboardType = textInputView.keyboardType;
@@ -443,12 +466,15 @@ - (void)invalidateInputAccessoryView
else {
textInputView.inputAccessoryView = nil;
}
+ [self reloadInputViewsIfNecessary];
+}
+- (void)reloadInputViewsIfNecessary
+{
// We have to call `reloadInputViews` for focused text inputs to update an accessory view.
- if (textInputView.isFirstResponder) {
- [textInputView reloadInputViews];
+ if (self.backedTextInputView.isFirstResponder) {
+ [self.backedTextInputView reloadInputViews];
}
-#endif
}
- (void)handleInputAccessoryDoneButton
diff --git a/Libraries/Text/TextInput/RCTBaseTextInputViewManager.m b/Libraries/Text/TextInput/RCTBaseTextInputViewManager.m
index e9b53769690b71..745fd83cb60967 100644
--- a/Libraries/Text/TextInput/RCTBaseTextInputViewManager.m
+++ b/Libraries/Text/TextInput/RCTBaseTextInputViewManager.m
@@ -53,6 +53,7 @@ @implementation RCTBaseTextInputViewManager
RCT_EXPORT_VIEW_PROPERTY(maxLength, NSNumber)
RCT_EXPORT_VIEW_PROPERTY(selectTextOnFocus, BOOL)
RCT_EXPORT_VIEW_PROPERTY(selection, RCTTextSelection)
+RCT_EXPORT_VIEW_PROPERTY(inputAccessoryViewID, NSString)
RCT_EXPORT_VIEW_PROPERTY(onChange, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onSelectionChange, RCTDirectEventBlock)
diff --git a/Libraries/Text/TextInput/RCTInputAccessoryView.h b/Libraries/Text/TextInput/RCTInputAccessoryView.h
new file mode 100644
index 00000000000000..b2517534baf3bb
--- /dev/null
+++ b/Libraries/Text/TextInput/RCTInputAccessoryView.h
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2015-present, Facebook, Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+#import
+
+@class RCTBridge;
+@class RCTInputAccessoryViewContent;
+
+@interface RCTInputAccessoryView : UIView
+
+- (instancetype)initWithBridge:(RCTBridge *)bridge;
+
+@property (nonatomic, readonly, strong) RCTInputAccessoryViewContent *content;
+
+@end
diff --git a/Libraries/Text/TextInput/RCTInputAccessoryView.m b/Libraries/Text/TextInput/RCTInputAccessoryView.m
new file mode 100644
index 00000000000000..a1902a80ea5fd4
--- /dev/null
+++ b/Libraries/Text/TextInput/RCTInputAccessoryView.m
@@ -0,0 +1,69 @@
+/**
+ * Copyright (c) 2015-present, Facebook, Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+#import "RCTInputAccessoryView.h"
+
+#import
+#import
+#import
+
+#import "RCTInputAccessoryViewContent.h"
+
+@implementation RCTInputAccessoryView
+{
+ BOOL _contentShouldBeFirstResponder;
+}
+
+- (instancetype)initWithBridge:(RCTBridge *)bridge
+{
+ if (self = [super init]) {
+ _content = [RCTInputAccessoryViewContent new];
+ RCTTouchHandler *const touchHandler = [[RCTTouchHandler alloc] initWithBridge:bridge];
+ [touchHandler attachToView:_content.inputAccessoryView];
+ [self addSubview:_content];
+ }
+ return self;
+}
+
+- (void)reactSetFrame:(CGRect)frame
+{
+ [_content.inputAccessoryView setFrame:frame];
+ [_content.contentView setFrame:frame];
+
+ if (_contentShouldBeFirstResponder) {
+ _contentShouldBeFirstResponder = NO;
+ [_content becomeFirstResponder];
+ }
+}
+
+- (void)insertReactSubview:(UIView *)subview atIndex:(NSInteger)index
+{
+ [super insertReactSubview:subview atIndex:index];
+ [_content insertReactSubview:subview atIndex:index];
+}
+
+- (void)removeReactSubview:(UIView *)subview
+{
+ [super removeReactSubview:subview];
+ [_content removeReactSubview:subview];
+}
+
+- (void)didUpdateReactSubviews
+{
+ // Do nothing, as subviews are managed by `insertReactSubview:atIndex:`
+}
+
+- (void)didSetProps:(NSArray *)changedProps
+{
+ // If the accessory view is not linked to a text input via nativeID, assume it is
+ // a standalone component that should get focus whenever it is rendered
+ if (![changedProps containsObject:@"nativeID"] && !self.nativeID) {
+ _contentShouldBeFirstResponder = YES;
+ }
+}
+
+@end
diff --git a/Libraries/Text/TextInput/RCTInputAccessoryViewContent.h b/Libraries/Text/TextInput/RCTInputAccessoryViewContent.h
new file mode 100644
index 00000000000000..7c24d010e2d270
--- /dev/null
+++ b/Libraries/Text/TextInput/RCTInputAccessoryViewContent.h
@@ -0,0 +1,14 @@
+/**
+ * Copyright (c) 2015-present, Facebook, Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+#import
+
+@interface RCTInputAccessoryViewContent : UIView
+
+@property (nonatomic, readwrite, retain) UIView *contentView;
+
+@end
diff --git a/Libraries/Text/TextInput/RCTInputAccessoryViewContent.m b/Libraries/Text/TextInput/RCTInputAccessoryViewContent.m
new file mode 100644
index 00000000000000..3fb7fca715c608
--- /dev/null
+++ b/Libraries/Text/TextInput/RCTInputAccessoryViewContent.m
@@ -0,0 +1,75 @@
+/**
+ * Copyright (c) 2015-present, Facebook, Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+#import "RCTInputAccessoryViewContent.h"
+
+#import
+
+@interface RCTInputAccessoryViewContent()
+
+// Overriding `inputAccessoryView` to `readwrite`.
+@property (nonatomic, readwrite, retain) UIView *inputAccessoryView;
+
+@end
+
+@implementation RCTInputAccessoryViewContent
+
+- (BOOL)canBecomeFirstResponder
+{
+ return true;
+}
+
+- (BOOL)becomeFirstResponder
+{
+ const BOOL becameFirstResponder = [super becomeFirstResponder];
+
+ #if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 /* __IPHONE_11_0 */
+ // Avoiding the home pill and notch (landscape mode) on iphoneX.
+ if (becameFirstResponder) {
+ if (@available(iOS 11.0, *)) {
+ [_contentView.bottomAnchor
+ constraintLessThanOrEqualToSystemSpacingBelowAnchor:_contentView.window.safeAreaLayoutGuide.bottomAnchor
+ multiplier:1.0f].active = YES;
+ [_contentView.leftAnchor
+ constraintLessThanOrEqualToSystemSpacingAfterAnchor:_contentView.window.safeAreaLayoutGuide.leftAnchor
+ multiplier:1.0f].active = YES;
+ [_contentView.rightAnchor
+ constraintLessThanOrEqualToSystemSpacingAfterAnchor:_contentView.window.safeAreaLayoutGuide.rightAnchor
+ multiplier:1.0f].active = YES;
+ }
+ }
+ #endif
+
+ return becameFirstResponder;
+}
+
+- (UIView *)inputAccessoryView
+{
+ if (!_inputAccessoryView) {
+ _inputAccessoryView = [UIView new];
+ _contentView = [UIView new];
+ [_inputAccessoryView addSubview:_contentView];
+ }
+ return _inputAccessoryView;
+}
+
+- (void)insertReactSubview:(UIView *)subview atIndex:(NSInteger)index
+{
+ [super insertReactSubview:subview atIndex:index];
+ [_contentView insertSubview:subview atIndex:index];
+}
+
+- (void)removeReactSubview:(UIView *)subview
+{
+ [super removeReactSubview:subview];
+ [subview removeFromSuperview];
+ if ([[_inputAccessoryView subviews] count] == 0 && [self isFirstResponder]) {
+ [self resignFirstResponder];
+ }
+}
+
+@end
diff --git a/Libraries/Text/TextInput/RCTInputAccessoryViewManager.h b/Libraries/Text/TextInput/RCTInputAccessoryViewManager.h
new file mode 100644
index 00000000000000..590723a5631716
--- /dev/null
+++ b/Libraries/Text/TextInput/RCTInputAccessoryViewManager.h
@@ -0,0 +1,12 @@
+/**
+ * Copyright (c) 2015-present, Facebook, Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+#import
+
+@interface RCTInputAccessoryViewManager : RCTViewManager
+
+@end
diff --git a/Libraries/Text/TextInput/RCTInputAccessoryViewManager.m b/Libraries/Text/TextInput/RCTInputAccessoryViewManager.m
new file mode 100644
index 00000000000000..b293889eb6de84
--- /dev/null
+++ b/Libraries/Text/TextInput/RCTInputAccessoryViewManager.m
@@ -0,0 +1,28 @@
+/**
+ * Copyright (c) 2015-present, Facebook, Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+#import "RCTInputAccessoryViewManager.h"
+
+#import "RCTInputAccessoryView.h"
+
+@implementation RCTInputAccessoryViewManager
+
+RCT_EXPORT_MODULE()
+
++ (BOOL)requiresMainQueueSetup
+{
+ return NO;
+}
+
+- (UIView *)view
+{
+ return [[RCTInputAccessoryView alloc] initWithBridge:self.bridge];
+}
+
+RCT_REMAP_VIEW_PROPERTY(backgroundColor, content.inputAccessoryView.backgroundColor, UIColor)
+
+@end
diff --git a/RNTester/js/TextInputExample.ios.js b/RNTester/js/TextInputExample.ios.js
index a55206355046da..9ace0a94ae6fab 100644
--- a/RNTester/js/TextInputExample.ios.js
+++ b/RNTester/js/TextInputExample.ios.js
@@ -9,6 +9,8 @@
*/
'use strict';
+const Button = require('Button');
+const InputAccessoryView = require('InputAccessoryView');
var React = require('react');
var ReactNative = require('react-native');
var {
@@ -91,6 +93,35 @@ class TextEventsExample extends React.Component<{}, $FlowFixMeState> {
}
}
+class TextInputAccessoryViewExample extends React.Component<{}, *> {
+ constructor(props) {
+ super(props);
+ this.state = {text: 'Placeholder Text'};
+ }
+
+ render() {
+ const inputAccessoryViewID = 'inputAccessoryView1';
+ return (
+
+ this.setState({text})}
+ value={this.state.text}
+ />
+
+
+
+
+
+ );
+ }
+}
+
class RewriteExample extends React.Component<$FlowFixMeProps, any> {
constructor(props) {
super(props);
@@ -485,6 +516,12 @@ exports.examples = [
return ;
}
},
+ {
+ title: 'Keyboard Accessory View',
+ render: function() {
+ return ;
+ }
+ },
{
title: 'Auto-capitalize',
render: function() {