Skip to content

Instantly share code, notes, and snippets.

@bleonard
Created August 1, 2015 06:31
Show Gist options
  • Star 10 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save bleonard/6770fbfe0394a34c864b to your computer and use it in GitHub Desktop.
Save bleonard/6770fbfe0394a34c864b to your computer and use it in GitHub Desktop.
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
Copy link

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