From bbc6139baf215da40da125a92965da03d7627cd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Bigio?= Date: Wed, 1 Jun 2016 13:33:48 -0700 Subject: [PATCH] Reverted commit D3348218 Summary: The TextInput when configured with `multiline=true` has a minor issue due to which if the initial text doesn't fit in one line, the input won't span multiple lines. The root cause of the problem is that the `onChange` event is not fired for the initial render. This issue has been reported on open-source: https://github.com/facebook/react-native/commit/481f560f64806ba3324cf722d6bf8c3f36ac74a5 This is an attempt to fix this problem by moving the logic to fire the event to the method that updates the content size. This way we can guarantee that anytime the content size changes we'll trigger the JS event. The downside of this approach is that it's possible that multiple events get fired for a single character change. As per the comment that was removed, this was already happening when adding a character that when rendered, would increase the content size. By moving the code to the new place, this can happen more often (twice per character tapped). Let me know if you think this is an issue. I don't know this code much, so please be careful reviewing. I'm happy to add more test cases I may have missed to the test plan :). Reviewed By: nicklockwood Differential Revision: D3348218 fbshipit-source-id: 6b457624c9126e771c326eac61cd1cdd6496671d --- Libraries/Text/RCTTextView.m | 41 +++++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/Libraries/Text/RCTTextView.m b/Libraries/Text/RCTTextView.m index a04bad457..d32463333 100644 --- a/Libraries/Text/RCTTextView.m +++ b/Libraries/Text/RCTTextView.m @@ -251,23 +251,6 @@ static NSAttributedString *removeReactTagFromString(NSAttributedString *string) size.height = [_textView sizeThatFits:size].height; _scrollView.contentSize = size; _textView.frame = (CGRect){CGPointZero, size}; - - NSUInteger textLength = _textView.text.length; - CGFloat contentHeight = _textView.contentSize.height; - if (textLength >= _previousTextLength) { - contentHeight = MAX(contentHeight, _previousContentHeight); - } - _previousTextLength = textLength; - _previousContentHeight = contentHeight; - _onChange(@{ - @"text": self.text, - @"contentSize": @{ - @"height": @(contentHeight), - @"width": @(_textView.contentSize.width) - }, - @"target": self.reactTag, - @"eventCount": @(_nativeEventCount), - }); } - (void)updatePlaceholder @@ -492,6 +475,30 @@ static NSAttributedString *removeReactTagFromString(NSAttributedString *string) if (!self.reactTag || !_onChange) { return; } + + // When the context size increases, iOS updates the contentSize twice; once + // with a lower height, then again with the correct height. To prevent a + // spurious event from being sent, we track the previous, and only send the + // update event if it matches our expectation that greater text length + // should result in increased height. This assumption is, of course, not + // necessarily true because shorter text might include more linebreaks, but + // in practice this works well enough. + NSUInteger textLength = textView.text.length; + CGFloat contentHeight = textView.contentSize.height; + if (textLength >= _previousTextLength) { + contentHeight = MAX(contentHeight, _previousContentHeight); + } + _previousTextLength = textLength; + _previousContentHeight = contentHeight; + _onChange(@{ + @"text": self.text, + @"contentSize": @{ + @"height": @(contentHeight), + @"width": @(textView.contentSize.width) + }, + @"target": self.reactTag, + @"eventCount": @(_nativeEventCount), + }); } - (void)textViewDidEndEditing:(UITextView *)textView