Fix multiline TextField initial render
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: 481f560f64
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: d3da3c0da1a0da9b9960625441191497e91d322e
This commit is contained in:
parent
88190f7e9d
commit
572a85fa24
|
@ -251,6 +251,23 @@ 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
|
||||
|
@ -475,30 +492,6 @@ 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
|
||||
|
|
Loading…
Reference in New Issue