Skip to content
Closed
16 changes: 16 additions & 0 deletions Examples/UIExplorer/TextInputExample.ios.js
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,15 @@ var styles = StyleSheet.create({
right: 5,
backgroundColor: 'red',
},
multilineAutoGrow: {
borderWidth: 0.5,
borderColor: '#0f0f0f',
flex: 1,
fontSize: 13,
height: 30,
padding: 4,
marginBottom: 4,
},
eventLabel: {
margin: 3,
fontSize: 12,
Expand Down Expand Up @@ -434,6 +443,13 @@ exports.examples = [
style={styles.multiline}>
<View style={styles.multilineChild}/>
</TextInput>
<TextInput
placeholder="auto growing multiline text input"
multiline={true}
autoGrow={true}
style={styles.multilineAutoGrow}
/>

</View>
);
}
Expand Down
12 changes: 12 additions & 0 deletions Libraries/Components/TextInput/TextInput.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ var onlyMultiline = {
onSelectionChange: true, // not supported in Open Source yet
onTextInput: true, // not supported in Open Source yet
children: true,
autoGrow: true,
maxHeight: true,
};

var notMultiline = {
Expand Down Expand Up @@ -100,6 +102,16 @@ var TextInput = React.createClass({
* If false, disables auto-correct. The default value is true.
*/
autoCorrect: PropTypes.bool,
/**
* If true, and the input is multiline, the input's height will grow automatically. The default value is false.
* @platorm ios
*/
autoGrow: PropTypes.bool,
/**
* The maximum height the input should grow to when autoGrow is true.
* @platorm ios
*/
maxHeight: PropTypes.number,
/**
* If true, focuses the input on componentDidMount.
* The default value is false.
Expand Down
5 changes: 4 additions & 1 deletion Libraries/Text/RCTTextView.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,16 @@
#import <UIKit/UIKit.h>

#import "RCTView.h"
#import "RCTUIManager.h"
#import "UIView+React.h"

@class RCTEventDispatcher;

@interface RCTTextView : RCTView <UITextViewDelegate>

@property (nonatomic, assign) BOOL autoCorrect;
@property (nonatomic, assign) BOOL autoGrow;
@property (nonatomic, assign) float maxHeight;
@property (nonatomic, assign) BOOL clearTextOnFocus;
@property (nonatomic, assign) BOOL selectTextOnFocus;
@property (nonatomic, assign) UIEdgeInsets contentInset;
Expand All @@ -28,6 +31,6 @@
@property (nonatomic, assign) NSInteger mostRecentEventCount;
@property (nonatomic, strong) NSNumber *maxLength;

- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithBridge:(RCTBridge *)bridge NS_DESIGNATED_INITIALIZER;

@end
65 changes: 62 additions & 3 deletions Libraries/Text/RCTTextView.m
Original file line number Diff line number Diff line change
Expand Up @@ -12,25 +12,32 @@
#import "RCTConvert.h"
#import "RCTEventDispatcher.h"
#import "RCTUtils.h"
#import "RCTUIManager.h"
#import "UIView+React.h"

@implementation RCTTextView
{
RCTBridge *_bridge;
RCTEventDispatcher *_eventDispatcher;
BOOL _jsRequestingFirstResponder;
BOOL _autoGrow;
float _origHeight;
float _maxHeight;
NSString *_placeholder;
UITextView *_placeholderView;
UITextView *_textView;
NSInteger _nativeEventCount;
}

- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher
- (instancetype)initWithBridge:(RCTBridge *)bridge
{
RCTAssertParam(eventDispatcher);
RCTAssertParam(bridge);

if ((self = [super initWithFrame:CGRectZero])) {
_bridge = bridge;
_autoGrow = false;
_contentInset = UIEdgeInsetsZero;
_eventDispatcher = eventDispatcher;
_eventDispatcher = _bridge.eventDispatcher;
_placeholderTextColor = [self defaultPlaceholderTextColor];

_textView = [[UITextView alloc] initWithFrame:self.bounds];
Expand Down Expand Up @@ -67,6 +74,10 @@ - (void)updateFrames

_textView.textContainerInset = adjustedTextContainerInset;
_placeholderView.textContainerInset = adjustedTextContainerInset;

if (! _origHeight) {
_origHeight = self.frame.size.height;
}
}

- (void)updatePlaceholder
Expand All @@ -89,6 +100,40 @@ - (void)updatePlaceholder
}
}

- (void)updateTextViewFrame
{
if (self.superview == nil) {
return;
}

if (_autoGrow) {
if (CGRectIsEmpty(self.frame)) {
return;
}

float currentHeight = _textView.frame.size.height;
float newHeight;

[_textView sizeToFit];

if (_textView.frame.size.height >= _origHeight) {
newHeight = _textView.frame.size.height;
} else {
newHeight = _origHeight;
}

if (_maxHeight > _origHeight) {
newHeight = fminf(newHeight, _maxHeight);
}

if (newHeight != currentHeight) {
CGRect newFrame = CGRectMake(0, 0, self.frame.size.width, newHeight);
[_bridge.uiManager setFrame:newFrame
forView:self];
}
}
}

- (UIFont *)font
{
return _textView.font;
Expand All @@ -98,6 +143,7 @@ - (void)setFont:(UIFont *)font
{
_textView.font = font;
[self updatePlaceholder];
[self updateTextViewFrame];
}

- (UIColor *)textColor
Expand Down Expand Up @@ -169,6 +215,7 @@ - (void)setText:(NSString *)text
UITextRange *selection = _textView.selectedTextRange;
_textView.text = text;
[self _setPlaceholderVisibility];
[self updateTextViewFrame];
_textView.selectedTextRange = selection; // maintain cursor position/selection - this is robust to out of bounds
} else if (eventLag > RCTTextUpdateLagWarningThreshold) {
RCTLogWarn(@"Native TextInput(%@) is %zd events ahead of JS - try to make your JS faster.", self.text, eventLag);
Expand All @@ -194,6 +241,16 @@ - (BOOL)autoCorrect
return _textView.autocorrectionType == UITextAutocorrectionTypeYes;
}

- (void)setAutoGrow:(BOOL)autoGrow
{
_autoGrow = autoGrow;
}

- (void)setMaxHeight:(float)maxHeight
{
_maxHeight = maxHeight;
}

- (BOOL)textViewShouldBeginEditing:(UITextView *)textView
{
if (_selectTextOnFocus) {
Expand All @@ -220,6 +277,8 @@ - (void)textViewDidBeginEditing:(UITextView *)textView
- (void)textViewDidChange:(UITextView *)textView
{
[self _setPlaceholderVisibility];
[self updateTextViewFrame];

_nativeEventCount++;
[_eventDispatcher sendTextEventWithType:RCTTextEventTypeChange
reactTag:self.reactTag
Expand Down
4 changes: 3 additions & 1 deletion Libraries/Text/RCTTextViewManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,12 @@ @implementation RCTTextViewManager

- (UIView *)view
{
return [[RCTTextView alloc] initWithEventDispatcher:self.bridge.eventDispatcher];
return [[RCTTextView alloc] initWithBridge:self.bridge];
}

RCT_EXPORT_VIEW_PROPERTY(autoCorrect, BOOL)
RCT_EXPORT_VIEW_PROPERTY(autoGrow, BOOL)
RCT_EXPORT_VIEW_PROPERTY(maxHeight, float)
RCT_REMAP_VIEW_PROPERTY(editable, textView.editable, BOOL)
RCT_EXPORT_VIEW_PROPERTY(placeholder, NSString)
RCT_EXPORT_VIEW_PROPERTY(placeholderTextColor, UIColor)
Expand Down