Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
var React = require('react-native')
var {
TextInput
} = React;
var RCTUIManager = require('NativeModules').UIManager;
var findNodeHandle = require('findNodeHandle');
var ExpandingTextInput = React.createClass({
propTypes: React.TextInput.propTypes,
getInitialState: function() {
return {
height: 50
};
},
setNativeProps: function(nativeProps) {
var input = this.refs.input;
input.setNativeProps(nativeProps);
if (nativeProps.text !== undefined) {
this.resetHeight();
}
},
onMeasureTextHeight: function(height) {
if (this.isMounted()) {
// console.log("onMeasureTextHeight: " + height);
// it's not clear to me why this is needed but it seems to help
// the height looks good at 50 and Xcode says it's 38
height += 12;
if (height < 50) height = 50;
if (height > 120) height = 120;
if (this.state.height !== height) {
this.setState({height: height});
}
}
},
resetHeight: function() {
RCTUIManager.measureTextHeight(
findNodeHandle(this.refs.input.refs.input),
this.onMeasureTextHeight
);
},
onChange: function(event) {
//console.log('ExpandingTextInput.onChange');
this.resetHeight();
if (this.props.onChange) this.props.onChange(event);
},
render: function() {
var passedStyle = this.props.style || {};
return (
<TextInput
{...this.props}
placeholder={"Enter message"}
onChange={this.onChange}
multiline={true}
ref="input"
style={[styles.input, passedStyle, {height: this.state.height}]}
/>
);
}
});
var styles = StyleSheet.create({
input: {
padding: 10,
flex: 1,
fontSize: 18
}
});
module.exports = ExpandingTextInput;
//
// RCTUIManager+TextView.m
// Tasker
//
// Created by Brian Leonard on 7/31/15.
//
#import <UIKit/UIKit.h>
#import "RCTUIManager.h"
#import "RCTSparseArray.h"
#import "UIView+React.h"
@interface RCTUIManager (TextView)
@end
@implementation RCTUIManager (TextView)
/**
* Returns information about the content inside of a RCTTextView
*/
RCT_EXPORT_METHOD(measureTextHeight:(NSNumber *)reactTag
callback:(RCTResponseSenderBlock)callback)
{
if (!callback) {
RCTLogError(@"Called measure with no callback");
return;
}
[self addUIBlock:^(__unused RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {
UIView *view = viewRegistry[reactTag];
if (!view) {
RCTLogError(@"measureTextContent cannot find view with tag #%@", reactTag);
return;
}
UIView *rootView = view;
while (rootView && ![rootView isReactRootView]) {
rootView = rootView.superview;
}
UITextView *textView = nil;
NSArray *subviews = [view subviews];
for(UIView *subview in subviews) {
if ([subview isKindOfClass:[UITextView class]]) {
UITextView * test = (UITextView *)subview;
if (![test isHidden]) { // placeholder is hidden
textView = test;
break;
}
}
}
if (!textView) {
RCTLogError(@"measureTextContent cannot find UITextView from tag #%@", reactTag);
return;
}
// http://stackoverflow.com/questions/19046969/uitextview-content-size-different-in-ios7
CGFloat measuredHeight;
if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_6_1)
{
// This is the code for iOS 7. contentSize no longer returns the correct value, so
// we have to calculate it.
//
// This is partly borrowed from HPGrowingTextView, but I've replaced the
// magic fudge factors with the calculated values (having worked out where
// they came from)
CGRect frame = textView.bounds;
// Take account of the padding added around the text.
UIEdgeInsets textContainerInsets = textView.textContainerInset;
UIEdgeInsets contentInsets = textView.contentInset;
CGFloat leftRightPadding = textContainerInsets.left + textContainerInsets.right + textView.textContainer.lineFragmentPadding * 2 + contentInsets.left + contentInsets.right;
CGFloat topBottomPadding = textContainerInsets.top + textContainerInsets.bottom + contentInsets.top + contentInsets.bottom;
frame.size.width -= leftRightPadding;
frame.size.height -= topBottomPadding;
NSString *textToMeasure = textView.text;
if ([textToMeasure hasSuffix:@"\n"])
{
textToMeasure = [NSString stringWithFormat:@"%@-", textView.text];
}
// NSString class method: boundingRectWithSize:options:attributes:context is
// available only on ios7.0 sdk.
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
[paragraphStyle setLineBreakMode:NSLineBreakByWordWrapping];
NSDictionary *attributes = @{ NSFontAttributeName: textView.font, NSParagraphStyleAttributeName : paragraphStyle };
CGRect size = [textToMeasure boundingRectWithSize:CGSizeMake(CGRectGetWidth(frame), MAXFLOAT)
options:NSStringDrawingUsesLineFragmentOrigin
attributes:attributes
context:nil];
measuredHeight = ceilf(CGRectGetHeight(size) + topBottomPadding);
}
else
{
measuredHeight = textView.contentSize.height;
}
callback(@[ @(measuredHeight) ]);
}];
}
@end

kevando commented Aug 21, 2016

Do you mind if I ask how to install this? I've never created a bridge module before and all my attempts to add this .m file have not worked. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment