Support Input Accessory View (iOS Only) [1/N]
Reviewed By: mmmulani Differential Revision: D6886573 fbshipit-source-id: 71e1f812b1cc1698e4380211a6cedd59011b5495
This commit is contained in:
parent
c87d03a8b2
commit
38197c8230
|
@ -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 (
|
||||||
|
* <View>
|
||||||
|
* <ScrollView keyboardDismissMode="interactive">
|
||||||
|
* <TextInput
|
||||||
|
* style={{
|
||||||
|
* padding: 10,
|
||||||
|
* paddingTop: 50,
|
||||||
|
* }}
|
||||||
|
* inputAccessoryViewID=inputAccessoryViewID
|
||||||
|
* onChangeText={text => this.setState({text})}
|
||||||
|
* value={this.state.text}
|
||||||
|
* />
|
||||||
|
* </ScrollView>
|
||||||
|
* <InputAccessoryView nativeID=inputAccessoryViewID>
|
||||||
|
* <Button
|
||||||
|
* onPress={() => this.setState({text: 'Placeholder Text'})}
|
||||||
|
* title="Reset Text"
|
||||||
|
* />
|
||||||
|
* </InputAccessoryView>
|
||||||
|
* </View>
|
||||||
|
* );
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* // 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<Props> {
|
||||||
|
render(): React.Node {
|
||||||
|
if (React.Children.count(this.props.children) === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<RCTInputAccessoryView
|
||||||
|
style={[this.props.style, styles.container]}
|
||||||
|
nativeID={this.props.nativeID}
|
||||||
|
backgroundColor={this.props.backgroundColor}>
|
||||||
|
{this.props.children}
|
||||||
|
</RCTInputAccessoryView>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
position: 'absolute',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = InputAccessoryView;
|
|
@ -590,6 +590,13 @@ const TextInput = createReactClass({
|
||||||
* This property is supported only for single-line TextInput component on iOS.
|
* This property is supported only for single-line TextInput component on iOS.
|
||||||
*/
|
*/
|
||||||
caretHidden: PropTypes.bool,
|
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 {
|
getDefaultProps(): Object {
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -103,6 +103,9 @@
|
||||||
5956B1A6200FF35C008D9D16 /* RCTUITextField.m in Sources */ = {isa = PBXBuildFile; fileRef = 5956B103200FEBA9008D9D16 /* RCTUITextField.m */; };
|
5956B1A6200FF35C008D9D16 /* RCTUITextField.m in Sources */ = {isa = PBXBuildFile; fileRef = 5956B103200FEBA9008D9D16 /* RCTUITextField.m */; };
|
||||||
5956B1A7200FF35C008D9D16 /* RCTVirtualTextShadowView.m in Sources */ = {isa = PBXBuildFile; fileRef = 5956B12E200FEBAA008D9D16 /* RCTVirtualTextShadowView.m */; };
|
5956B1A7200FF35C008D9D16 /* RCTVirtualTextShadowView.m in Sources */ = {isa = PBXBuildFile; fileRef = 5956B12E200FEBAA008D9D16 /* RCTVirtualTextShadowView.m */; };
|
||||||
5956B1A8200FF35C008D9D16 /* RCTVirtualTextViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 5956B12B200FEBAA008D9D16 /* RCTVirtualTextViewManager.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 */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXCopyFilesBuildPhase section */
|
/* Begin PBXCopyFilesBuildPhase section */
|
||||||
|
@ -229,6 +232,12 @@
|
||||||
5956B12D200FEBAA008D9D16 /* RCTVirtualTextViewManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTVirtualTextViewManager.h; sourceTree = "<group>"; };
|
5956B12D200FEBAA008D9D16 /* RCTVirtualTextViewManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTVirtualTextViewManager.h; sourceTree = "<group>"; };
|
||||||
5956B12E200FEBAA008D9D16 /* RCTVirtualTextShadowView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTVirtualTextShadowView.m; sourceTree = "<group>"; };
|
5956B12E200FEBAA008D9D16 /* RCTVirtualTextShadowView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTVirtualTextShadowView.m; sourceTree = "<group>"; };
|
||||||
5956B12F200FEBAA008D9D16 /* RCTConvert+Text.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "RCTConvert+Text.m"; sourceTree = "<group>"; };
|
5956B12F200FEBAA008D9D16 /* RCTConvert+Text.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "RCTConvert+Text.m"; sourceTree = "<group>"; };
|
||||||
|
8F2807C1202D2B6A005D65E6 /* RCTInputAccessoryViewManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTInputAccessoryViewManager.m; sourceTree = "<group>"; };
|
||||||
|
8F2807C2202D2B6A005D65E6 /* RCTInputAccessoryViewContent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTInputAccessoryViewContent.h; sourceTree = "<group>"; };
|
||||||
|
8F2807C3202D2B6A005D65E6 /* RCTInputAccessoryView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTInputAccessoryView.m; sourceTree = "<group>"; };
|
||||||
|
8F2807C4202D2B6A005D65E6 /* RCTInputAccessoryView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTInputAccessoryView.h; sourceTree = "<group>"; };
|
||||||
|
8F2807C5202D2B6B005D65E6 /* RCTInputAccessoryViewContent.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTInputAccessoryViewContent.m; sourceTree = "<group>"; };
|
||||||
|
8F2807C6202D2B6B005D65E6 /* RCTInputAccessoryViewManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTInputAccessoryViewManager.h; sourceTree = "<group>"; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXGroup section */
|
/* Begin PBXGroup section */
|
||||||
|
@ -274,6 +283,12 @@
|
||||||
5956B0FF200FEBA9008D9D16 /* TextInput */ = {
|
5956B0FF200FEBA9008D9D16 /* TextInput */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
8F2807C4202D2B6A005D65E6 /* RCTInputAccessoryView.h */,
|
||||||
|
8F2807C3202D2B6A005D65E6 /* RCTInputAccessoryView.m */,
|
||||||
|
8F2807C2202D2B6A005D65E6 /* RCTInputAccessoryViewContent.h */,
|
||||||
|
8F2807C5202D2B6B005D65E6 /* RCTInputAccessoryViewContent.m */,
|
||||||
|
8F2807C6202D2B6B005D65E6 /* RCTInputAccessoryViewManager.h */,
|
||||||
|
8F2807C1202D2B6A005D65E6 /* RCTInputAccessoryViewManager.m */,
|
||||||
5956B113200FEBA9008D9D16 /* Multiline */,
|
5956B113200FEBA9008D9D16 /* Multiline */,
|
||||||
5956B10C200FEBA9008D9D16 /* RCTBackedTextInputDelegate.h */,
|
5956B10C200FEBA9008D9D16 /* RCTBackedTextInputDelegate.h */,
|
||||||
5956B107200FEBA9008D9D16 /* RCTBackedTextInputDelegateAdapter.h */,
|
5956B107200FEBA9008D9D16 /* RCTBackedTextInputDelegateAdapter.h */,
|
||||||
|
@ -465,8 +480,11 @@
|
||||||
5956B140200FEBAA008D9D16 /* RCTTextShadowView.m in Sources */,
|
5956B140200FEBAA008D9D16 /* RCTTextShadowView.m in Sources */,
|
||||||
5956B131200FEBAA008D9D16 /* RCTRawTextViewManager.m in Sources */,
|
5956B131200FEBAA008D9D16 /* RCTRawTextViewManager.m in Sources */,
|
||||||
5956B137200FEBAA008D9D16 /* RCTBaseTextInputShadowView.m in Sources */,
|
5956B137200FEBAA008D9D16 /* RCTBaseTextInputShadowView.m in Sources */,
|
||||||
|
8F2807C7202D2B6B005D65E6 /* RCTInputAccessoryViewManager.m in Sources */,
|
||||||
5956B146200FEBAA008D9D16 /* RCTConvert+Text.m in Sources */,
|
5956B146200FEBAA008D9D16 /* RCTConvert+Text.m in Sources */,
|
||||||
|
8F2807C9202D2B6B005D65E6 /* RCTInputAccessoryViewContent.m in Sources */,
|
||||||
5956B13F200FEBAA008D9D16 /* RCTTextAttributes.m in Sources */,
|
5956B13F200FEBAA008D9D16 /* RCTTextAttributes.m in Sources */,
|
||||||
|
8F2807C8202D2B6B005D65E6 /* RCTInputAccessoryView.m in Sources */,
|
||||||
5956B143200FEBAA008D9D16 /* RCTTextView.m in Sources */,
|
5956B143200FEBAA008D9D16 /* RCTTextView.m in Sources */,
|
||||||
5956B13C200FEBAA008D9D16 /* RCTUITextView.m in Sources */,
|
5956B13C200FEBAA008D9D16 /* RCTUITextView.m in Sources */,
|
||||||
5956B136200FEBAA008D9D16 /* RCTBackedTextInputDelegateAdapter.m in Sources */,
|
5956B136200FEBAA008D9D16 /* RCTBackedTextInputDelegateAdapter.m in Sources */,
|
||||||
|
|
|
@ -46,6 +46,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||||
@property (nonatomic, copy) RCTTextSelection *selection;
|
@property (nonatomic, copy) RCTTextSelection *selection;
|
||||||
@property (nonatomic, strong, nullable) NSNumber *maxLength;
|
@property (nonatomic, strong, nullable) NSNumber *maxLength;
|
||||||
@property (nonatomic, copy) NSAttributedString *attributedText;
|
@property (nonatomic, copy) NSAttributedString *attributedText;
|
||||||
|
@property (nonatomic, copy) NSString *inputAccessoryViewID;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,8 @@
|
||||||
#import <React/RCTUtils.h>
|
#import <React/RCTUtils.h>
|
||||||
#import <React/UIView+React.h>
|
#import <React/UIView+React.h>
|
||||||
|
|
||||||
|
#import "RCTInputAccessoryView.h"
|
||||||
|
#import "RCTInputAccessoryViewContent.h"
|
||||||
#import "RCTTextAttributes.h"
|
#import "RCTTextAttributes.h"
|
||||||
#import "RCTTextSelection.h"
|
#import "RCTTextSelection.h"
|
||||||
|
|
||||||
|
@ -400,12 +402,33 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithFrame:(CGRect)frame)
|
||||||
|
|
||||||
- (void)didSetProps:(NSArray<NSString *> *)changedProps
|
- (void)didSetProps:(NSArray<NSString *> *)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)invalidateInputAccessoryView
|
- (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)setDefaultInputAccessoryView
|
||||||
{
|
{
|
||||||
#if !TARGET_OS_TV
|
|
||||||
UIView<RCTBackedTextInputViewProtocol> *textInputView = self.backedTextInputView;
|
UIView<RCTBackedTextInputViewProtocol> *textInputView = self.backedTextInputView;
|
||||||
UIKeyboardType keyboardType = textInputView.keyboardType;
|
UIKeyboardType keyboardType = textInputView.keyboardType;
|
||||||
|
|
||||||
|
@ -443,12 +466,15 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithFrame:(CGRect)frame)
|
||||||
else {
|
else {
|
||||||
textInputView.inputAccessoryView = nil;
|
textInputView.inputAccessoryView = nil;
|
||||||
}
|
}
|
||||||
|
[self reloadInputViewsIfNecessary];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)reloadInputViewsIfNecessary
|
||||||
|
{
|
||||||
// We have to call `reloadInputViews` for focused text inputs to update an accessory view.
|
// We have to call `reloadInputViews` for focused text inputs to update an accessory view.
|
||||||
if (textInputView.isFirstResponder) {
|
if (self.backedTextInputView.isFirstResponder) {
|
||||||
[textInputView reloadInputViews];
|
[self.backedTextInputView reloadInputViews];
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)handleInputAccessoryDoneButton
|
- (void)handleInputAccessoryDoneButton
|
||||||
|
|
|
@ -53,6 +53,7 @@ RCT_EXPORT_VIEW_PROPERTY(clearTextOnFocus, BOOL)
|
||||||
RCT_EXPORT_VIEW_PROPERTY(maxLength, NSNumber)
|
RCT_EXPORT_VIEW_PROPERTY(maxLength, NSNumber)
|
||||||
RCT_EXPORT_VIEW_PROPERTY(selectTextOnFocus, BOOL)
|
RCT_EXPORT_VIEW_PROPERTY(selectTextOnFocus, BOOL)
|
||||||
RCT_EXPORT_VIEW_PROPERTY(selection, RCTTextSelection)
|
RCT_EXPORT_VIEW_PROPERTY(selection, RCTTextSelection)
|
||||||
|
RCT_EXPORT_VIEW_PROPERTY(inputAccessoryViewID, NSString)
|
||||||
|
|
||||||
RCT_EXPORT_VIEW_PROPERTY(onChange, RCTBubblingEventBlock)
|
RCT_EXPORT_VIEW_PROPERTY(onChange, RCTBubblingEventBlock)
|
||||||
RCT_EXPORT_VIEW_PROPERTY(onSelectionChange, RCTDirectEventBlock)
|
RCT_EXPORT_VIEW_PROPERTY(onSelectionChange, RCTDirectEventBlock)
|
||||||
|
|
|
@ -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 <UIKit/UIKit.h>
|
||||||
|
|
||||||
|
@class RCTBridge;
|
||||||
|
@class RCTInputAccessoryViewContent;
|
||||||
|
|
||||||
|
@interface RCTInputAccessoryView : UIView
|
||||||
|
|
||||||
|
- (instancetype)initWithBridge:(RCTBridge *)bridge;
|
||||||
|
|
||||||
|
@property (nonatomic, readonly, strong) RCTInputAccessoryViewContent *content;
|
||||||
|
|
||||||
|
@end
|
|
@ -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 <React/RCTBridge.h>
|
||||||
|
#import <React/RCTTouchHandler.h>
|
||||||
|
#import <React/UIView+React.h>
|
||||||
|
|
||||||
|
#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<NSString *> *)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
|
|
@ -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 <UIKit/UIKit.h>
|
||||||
|
|
||||||
|
@interface RCTInputAccessoryViewContent : UIView
|
||||||
|
|
||||||
|
@property (nonatomic, readwrite, retain) UIView *contentView;
|
||||||
|
|
||||||
|
@end
|
|
@ -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 <React/UIView+React.h>
|
||||||
|
|
||||||
|
@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
|
|
@ -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 <React/RCTViewManager.h>
|
||||||
|
|
||||||
|
@interface RCTInputAccessoryViewManager : RCTViewManager
|
||||||
|
|
||||||
|
@end
|
|
@ -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
|
|
@ -9,6 +9,8 @@
|
||||||
*/
|
*/
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
const Button = require('Button');
|
||||||
|
const InputAccessoryView = require('InputAccessoryView');
|
||||||
var React = require('react');
|
var React = require('react');
|
||||||
var ReactNative = require('react-native');
|
var ReactNative = require('react-native');
|
||||||
var {
|
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 (
|
||||||
|
<View>
|
||||||
|
<TextInput
|
||||||
|
style={styles.default}
|
||||||
|
inputAccessoryViewID={inputAccessoryViewID}
|
||||||
|
onChangeText={text => this.setState({text})}
|
||||||
|
value={this.state.text}
|
||||||
|
/>
|
||||||
|
<InputAccessoryView nativeID={inputAccessoryViewID}>
|
||||||
|
<View style={{backgroundColor: 'white'}}>
|
||||||
|
<Button
|
||||||
|
onPress={() => this.setState({text: 'Placeholder Text'})}
|
||||||
|
title="Reset Text"
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
</InputAccessoryView>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class RewriteExample extends React.Component<$FlowFixMeProps, any> {
|
class RewriteExample extends React.Component<$FlowFixMeProps, any> {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
@ -485,6 +516,12 @@ exports.examples = [
|
||||||
return <RewriteExampleInvalidCharacters />;
|
return <RewriteExampleInvalidCharacters />;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: 'Keyboard Accessory View',
|
||||||
|
render: function() {
|
||||||
|
return <TextInputAccessoryViewExample />;
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: 'Auto-capitalize',
|
title: 'Auto-capitalize',
|
||||||
render: function() {
|
render: function() {
|
||||||
|
|
Loading…
Reference in New Issue