Skip to content

Commit

Permalink
Fix multi-character TextInput
Browse files Browse the repository at this point in the history
Reviewed By: hnery

Differential Revision: D3457105

fbshipit-source-id: dcb364123ed82842d4fb2dee9108f2805249a8f9
  • Loading branch information
javache authored and Facebook Github Bot 1 committed Jun 23, 2016
1 parent 57d85f1 commit cc95927
Show file tree
Hide file tree
Showing 5 changed files with 24 additions and 66 deletions.
23 changes: 19 additions & 4 deletions Libraries/Text/RCTShadowText.m
Expand Up @@ -18,6 +18,7 @@
#import "RCTText.h"
#import "RCTUtils.h"
#import "RCTConvert.h"
#import "RCTTextView.h"

NSString *const RCTShadowViewAttributeName = @"RCTShadowViewAttributeName";
NSString *const RCTIsHighlightedAttributeName = @"IsHighlightedAttributeName";
Expand Down Expand Up @@ -98,10 +99,24 @@ - (void)contentSizeMultiplierDidChange:(NSNotification *)note
UIEdgeInsets padding = self.paddingAsInsets;
CGFloat width = self.frame.size.width - (padding.left + padding.right);

NSNumber *parentTag = [[self reactSuperview] reactTag];
NSTextStorage *textStorage = [self buildTextStorageForWidth:width widthMode:CSS_MEASURE_MODE_EXACTLY];
[applierBlocks addObject:^(NSDictionary<NSNumber *, RCTText *> *viewRegistry) {
RCTText *view = viewRegistry[self.reactTag];
[applierBlocks addObject:^(NSDictionary<NSNumber *, UIView *> *viewRegistry) {
RCTText *view = (RCTText *)viewRegistry[self.reactTag];
view.textStorage = textStorage;

/**
* NOTE: this logic is included to support rich text editing inside multiline
* `<TextInput>` controls. It is required in order to ensure that the
* textStorage (aka attributed string) is copied over from the RCTShadowText
* to the RCTText view in time to be used to update the editable text content.
* TODO: we should establish a delegate relationship betweeen RCTTextView
* and its contaned RCTText element when they get inserted and get rid of this
*/
UIView *parentView = viewRegistry[parentTag];
if ([parentView respondsToSelector:@selector(performTextUpdate)]) {
[(RCTTextView *)parentView performTextUpdate];
}
}];

return parentProperties;
Expand Down Expand Up @@ -167,13 +182,13 @@ - (NSTextStorage *)buildTextStorageForWidth:(CGFloat)width widthMode:(css_measur

NSTextContainer *textContainer = [NSTextContainer new];
textContainer.lineFragmentPadding = 0.0;

if (_numberOfLines > 0) {
textContainer.lineBreakMode = _lineBreakMode;
} else {
textContainer.lineBreakMode = NSLineBreakByClipping;
}

textContainer.maximumNumberOfLines = _numberOfLines;
textContainer.size = (CGSize){widthMode == CSS_MEASURE_MODE_UNDEFINED ? CGFLOAT_MAX : width, CGFLOAT_MAX};

Expand Down
1 change: 0 additions & 1 deletion Libraries/Text/RCTText.m
Expand Up @@ -155,7 +155,6 @@ - (NSNumber *)reactTagAtPoint:(CGPoint)point
return reactTag;
}


- (void)didMoveToWindow
{
[super didMoveToWindow];
Expand Down
61 changes: 1 addition & 60 deletions Libraries/Text/RCTTextManager.m
Expand Up @@ -78,7 +78,6 @@ - (RCTShadowView *)shadowView

- (RCTViewManagerUIBlock)uiBlockToAmendWithShadowViewRegistry:(NSDictionary<NSNumber *, RCTShadowView *> *)shadowViewRegistry
{
NSMutableSet *textViewTagsToUpdate = [NSMutableSet new];
for (RCTShadowView *rootView in shadowViewRegistry.allValues) {
if (![rootView isReactRootView]) {
// This isn't a root view
Expand All @@ -103,19 +102,6 @@ - (RCTViewManagerUIBlock)uiBlockToAmendWithShadowViewRegistry:(NSDictionary<NSNu
RCTLogError(@"Raw text cannot be used outside of a <Text> tag. Not rendering string: '%@'",
[(RCTShadowRawText *)shadowView text]);
} else {
NSNumber *reactTag = shadowView.reactTag;
// This isn't pretty, but hopefully it's temporary
// the problem is, there's no easy way (besides the viewName)
// to tell from the shadowView if the view is an RKTextView
if ([shadowView.viewName hasSuffix:@"TextView"]) {
// Add to textViewTagsToUpdate only if has a RCTShadowText subview
for (RCTShadowView *subview in shadowView.reactSubviews) {
if ([subview isKindOfClass:[RCTShadowText class]]) {
[textViewTagsToUpdate addObject:reactTag];
break;
}
}
}
for (RCTShadowView *child in [shadowView reactSubviews]) {
if ([child isTextDirty]) {
[queue addObject:child];
Expand All @@ -127,52 +113,7 @@ - (RCTViewManagerUIBlock)uiBlockToAmendWithShadowViewRegistry:(NSDictionary<NSNu
}
}

/**
* NOTE: this logic is included to support rich text editing inside multiline
* `<TextInput>` controls. It is required in order to ensure that the
* textStorage (aka attributed string) is copied over from the RCTShadowText
* to the RCTText view in time to be used to update the editable text content.
*/
if (textViewTagsToUpdate.count) {

NSMutableArray<RCTViewManagerUIBlock> *uiBlocks = [NSMutableArray new];
for (NSNumber *reactTag in textViewTagsToUpdate) {
RCTShadowView *shadowTextView = shadowViewRegistry[reactTag];
RCTShadowText *shadowText;
for (RCTShadowText *subview in shadowTextView.reactSubviews) {
if ([subview isKindOfClass:[RCTShadowText class]]) {
shadowText = subview;
break;
}
}

UIEdgeInsets padding = shadowText.paddingAsInsets;
CGFloat width = shadowText.frame.size.width - (padding.left + padding.right);

NSTextStorage *textStorage = [shadowText buildTextStorageForWidth:width widthMode:CSS_MEASURE_MODE_EXACTLY];
[uiBlocks addObject:^(RCTUIManager *uiManager, NSDictionary<NSNumber *, RCTTextView *> *viewRegistry) {
RCTTextView *textView = viewRegistry[reactTag];
RCTText *text;
for (RCTText *subview in textView.reactSubviews) {
if ([subview isKindOfClass:[RCTText class]]) {
text = subview;
break;
}
}

text.textStorage = textStorage;
[textView performTextUpdate];
}];
}

return ^(RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry) {
for (RCTViewManagerUIBlock uiBlock in uiBlocks) {
uiBlock(uiManager, viewRegistry);
}
};
} else {
return nil;
}
return nil;
}

- (RCTViewManagerUIBlock)uiBlockToAmendWithShadowView:(RCTShadowText *)shadowView
Expand Down
3 changes: 3 additions & 0 deletions Libraries/Text/RCTTextView.m
Expand Up @@ -125,6 +125,8 @@ - (void)insertReactSubview:(UIView *)subview atIndex:(NSInteger)index
attrs[NSBackgroundColorAttributeName] = subview.backgroundColor;
_textView.typingAttributes = attrs;
}

[self performTextUpdate];
}
}

Expand All @@ -133,6 +135,7 @@ - (void)removeReactSubview:(UIView *)subview
[super removeReactSubview:subview];
if (_richTextView == subview) {
_richTextView = nil;
[self performTextUpdate];
}
}

Expand Down
2 changes: 1 addition & 1 deletion React/Views/RCTViewManager.h
Expand Up @@ -85,7 +85,7 @@ typedef void (^RCTViewManagerUIBlock)(RCTUIManager *uiManager, NSDictionary<NSNu
/**
* Called after view hierarchy manipulation has finished, and all shadow props
* have been set, but before layout has been performed. Useful for performing
* custo layout logic or tasks that involve walking the view hierarchy.
* custom layout logic or tasks that involve walking the view hierarchy.
* To be deprecated, hopefully.
*/
- (RCTViewManagerUIBlock)uiBlockToAmendWithShadowViewRegistry:(NSDictionary<NSNumber *, RCTShadowView *> *)shadowViewRegistry;
Expand Down

0 comments on commit cc95927

Please sign in to comment.