Permalink
Browse files

The New <Text> on iOS

Summary:
This is a complete rewrite of RCTText, the part of React Native which manages Text and TextInput components.

Key points:

* It's understandable now. It follows a simple architectural pattern, and it's easy to debug and iterate. Text flow layout is a first-class citizen in React Native layout system now, not just a wired special case. It also brings entirely new possibilities such as nested interleaving <Text> and <View> components.
* All <Text>-specific APIs were removed from UIManager and co (it's about ~16 public methods which were used exclusively only by <Text>).
* It relies on new Yoga measurement/cloning API and on-dirty handler. So, it removes built-in dirty propagation subsystem from RN completely.
* It caches string fragments properly and granularly on a per-node basis which makes updating text-containing components more performant.
* It does not instantiate UIView for virtual components which reduces memory utilization.
* It drastically improves <TextInput> capabilities (e.g. rich text inside single line <TextInput> is now supported).

Screenshots:
https://cl.ly/2j3r1V0L0324
https://cl.ly/3N2V3C3d3q3R

Reviewed By: mmmulani

Differential Revision: D6617326

fbshipit-source-id: 35d4d81b35c9870e9557d0211c0e934e6072a41e
  • Loading branch information...
shergin authored and facebook-github-bot committed Jan 24, 2018
1 parent cd263a2 commit 2716f53220f947c690d5f627286aad51313256a0
Showing with 2,425 additions and 2,078 deletions.
  1. +26 −0 Libraries/Text/BaseText/RCTBaseTextShadowView.h
  2. +125 −0 Libraries/Text/BaseText/RCTBaseTextShadowView.m
  3. +6 −2 Libraries/Text/{RCTFontAttributesDelegate.h → BaseText/RCTBaseTextViewManager.h}
  4. +57 −0 Libraries/Text/BaseText/RCTBaseTextViewManager.m
  5. +6 −2 Libraries/Text/RCTConvert+Text.h
  6. +0 −31 Libraries/Text/RCTFontAttributes.h
  7. +0 −112 Libraries/Text/RCTFontAttributes.m
  8. +322 −256 Libraries/Text/RCTText.xcodeproj/project.pbxproj
  9. +85 −0 Libraries/Text/RCTTextAttributes.h
  10. +270 −0 Libraries/Text/RCTTextAttributes.m
  11. +5 −1 Libraries/Text/{ → RawText}/RCTRawTextShadowView.h
  12. +7 −23 Libraries/Text/{ → RawText}/RCTRawTextShadowView.m
  13. +4 −0 Libraries/Text/{ → RawText}/RCTRawTextViewManager.h
  14. +5 −0 Libraries/Text/{ → RawText}/RCTRawTextViewManager.m
  15. +22 −0 Libraries/Text/Text/NSTextStorage+FontScaling.h
  16. +139 −0 Libraries/Text/Text/NSTextStorage+FontScaling.m
  17. +14 −41 Libraries/Text/Text/RCTTextShadowView.h
  18. +228 −570 Libraries/Text/Text/RCTTextShadowView.m
  19. +8 −3 Libraries/Text/Text/RCTTextView.h
  20. +63 −61 Libraries/Text/Text/RCTTextView.m
  21. +3 −1 Libraries/Text/Text/RCTTextViewManager.h
  22. +52 −95 Libraries/Text/Text/RCTTextViewManager.m
  23. +3 −21 Libraries/Text/TextInput/Multiline/RCTMultilineTextInputView.h
  24. +18 −340 Libraries/Text/TextInput/Multiline/RCTMultilineTextInputView.m
  25. +4 −0 Libraries/Text/TextInput/Multiline/RCTMultilineTextInputViewManager.h
  26. +0 −19 Libraries/Text/TextInput/Multiline/RCTMultilineTextInputViewManager.m
  27. +1 −1 Libraries/Text/TextInput/Multiline/RCTUITextView.h
  28. +3 −3 Libraries/Text/TextInput/Multiline/RCTUITextView.m
  29. +4 −0 Libraries/Text/TextInput/RCTBackedTextInputDelegate.h
  30. +6 −2 Libraries/Text/TextInput/RCTBackedTextInputDelegateAdapter.h
  31. +39 −39 Libraries/Text/TextInput/RCTBackedTextInputDelegateAdapter.m
  32. +12 −2 Libraries/Text/TextInput/RCTBackedTextInputViewProtocol.h
  33. +27 −0 Libraries/Text/TextInput/RCTBaseTextInputShadowView.h
  34. +251 −0 Libraries/Text/TextInput/RCTBaseTextInputShadowView.m
  35. +15 −29 Libraries/Text/TextInput/RCTBaseTextInputView.h
  36. +239 −79 Libraries/Text/TextInput/RCTBaseTextInputView.m
  37. +2 −2 Libraries/Text/TextInput/RCTBaseTextInputViewManager.h
  38. +71 −18 Libraries/Text/TextInput/RCTBaseTextInputViewManager.m
  39. +0 −19 Libraries/Text/TextInput/Singleline/RCTSinglelineTextInputShadowView.m
  40. +3 −9 Libraries/Text/TextInput/Singleline/RCTSinglelineTextInputView.h
  41. +8 −105 Libraries/Text/TextInput/Singleline/RCTSinglelineTextInputView.m
  42. +4 −0 Libraries/Text/TextInput/Singleline/RCTSinglelineTextInputViewManager.h
  43. +7 −15 Libraries/Text/TextInput/Singleline/RCTSinglelineTextInputViewManager.m
  44. +2 −2 .../{TextInput/Multiline/RCTMultilineTextInputShadowView.h → VirtualText/RCTVirtualTextShadowView.h}
  45. +75 −0 Libraries/Text/VirtualText/RCTVirtualTextShadowView.m
  46. +2 −2 ...extInput/Singleline/RCTSinglelineTextInputShadowView.h → VirtualText/RCTVirtualTextViewManager.h}
  47. +13 −4 ...{TextInput/Multiline/RCTMultilineTextInputShadowView.m → VirtualText/RCTVirtualTextViewManager.m}
  48. +21 −12 RNTester/js/TextExample.ios.js
  49. +108 −0 RNTester/js/TextInputExample.ios.js
  50. +2 −1 React/Modules/RCTAccessibilityManager.m
  51. +0 −48 React/Modules/RCTUIManager.m
  52. +0 −2 React/Views/RCTComponentData.h
  53. +0 −17 React/Views/RCTComponentData.m
  54. +5 −7 React/Views/RCTShadowView.h
  55. +33 −58 React/Views/RCTShadowView.m
  56. +0 −14 React/Views/RCTViewManager.h
  57. +0 −10 React/Views/RCTViewManager.m
@@ -0,0 +1,26 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import <React/RCTShadowView.h>
#import "RCTTextAttributes.h"
NS_ASSUME_NONNULL_BEGIN
extern NSString *const RCTBaseTextShadowViewEmbeddedShadowViewAttributeName;
@interface RCTBaseTextShadowView : RCTShadowView
@property (nonatomic, strong) RCTTextAttributes *textAttributes;
- (NSAttributedString *)attributedTextWithBaseTextAttributes:(nullable RCTTextAttributes *)baseTextAttributes;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,125 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import "RCTBaseTextShadowView.h"
#import <React/RCTShadowView+Layout.h>
#import "RCTRawTextShadowView.h"
#import "RCTVirtualTextShadowView.h"
NSString *const RCTBaseTextShadowViewEmbeddedShadowViewAttributeName = @"RCTBaseTextShadowViewEmbeddedShadowViewAttributeName";
@implementation RCTBaseTextShadowView
{
NSAttributedString *_Nullable _cachedAttributedText;
RCTTextAttributes *_Nullable _cachedTextAttributes;
}
- (instancetype)init
{
if (self = [super init]) {
_textAttributes = [RCTTextAttributes new];
}
return self;
}
- (void)setReactTag:(NSNumber *)reactTag
{
[super setReactTag:reactTag];
_textAttributes.tag = reactTag;
}
#pragma mark - attributedString
- (NSAttributedString *)attributedTextWithBaseTextAttributes:(nullable RCTTextAttributes *)baseTextAttributes
{
RCTTextAttributes *textAttributes;
if (baseTextAttributes) {
textAttributes = [baseTextAttributes copy];
[textAttributes applyTextAttributes:self.textAttributes];
} else {
textAttributes = [self.textAttributes copy];
}
if (_cachedAttributedText && [_cachedTextAttributes isEqual:textAttributes]) {
return _cachedAttributedText;
}
NSMutableAttributedString *attributedText = [NSMutableAttributedString new];
[attributedText beginEditing];
for (RCTShadowView *shadowView in self.reactSubviews) {
// Special Case: RCTRawTextShadowView
if ([shadowView isKindOfClass:[RCTRawTextShadowView class]]) {
RCTRawTextShadowView *rawTextShadowView = (RCTRawTextShadowView *)shadowView;
NSString *text = rawTextShadowView.text;
if (text) {
NSAttributedString *rawTextAttributedString =
[[NSAttributedString alloc] initWithString:rawTextShadowView.text
attributes:textAttributes.effectiveTextAttributes];
[attributedText appendAttributedString:rawTextAttributedString];
}
continue;
}
// Special Case: RCTBaseTextShadowView
if ([shadowView isKindOfClass:[RCTBaseTextShadowView class]]) {
RCTBaseTextShadowView *baseTextShadowView = (RCTBaseTextShadowView *)shadowView;
NSAttributedString *baseTextAttributedString =
[baseTextShadowView attributedTextWithBaseTextAttributes:textAttributes];
[attributedText appendAttributedString:baseTextAttributedString];
continue;
}
// Generic Case: Any RCTShadowView
NSTextAttachment *attachment = [NSTextAttachment new];
NSMutableAttributedString *embeddedShadowViewAttributedString = [NSMutableAttributedString new];
[embeddedShadowViewAttributedString beginEditing];
[embeddedShadowViewAttributedString appendAttributedString:[NSAttributedString attributedStringWithAttachment:attachment]];
[embeddedShadowViewAttributedString addAttribute:RCTBaseTextShadowViewEmbeddedShadowViewAttributeName
value:shadowView
range:(NSRange){0, embeddedShadowViewAttributedString.length}];
[embeddedShadowViewAttributedString endEditing];
[attributedText appendAttributedString:embeddedShadowViewAttributedString];
}
[attributedText endEditing];
[self clearLayout];
_cachedAttributedText = [attributedText copy];
_cachedTextAttributes = textAttributes;
return _cachedAttributedText;
}
- (void)dirtyLayout
{
[super dirtyLayout];
_cachedAttributedText = nil;
_cachedTextAttributes = nil;
}
- (void)didUpdateReactSubviews
{
[super didUpdateReactSubviews];
[self dirtyLayout];
}
- (void)didSetProps:(NSArray<NSString *> *)changedProps
{
[super didSetProps:changedProps];
[self dirtyLayout];
}
@end
@@ -7,8 +7,12 @@
* of patent rights can be found in the PATENTS file in the same directory.
*/
@protocol RCTFontAttributesDelegate <NSObject>
#import <React/RCTViewManager.h>
- (void)fontAttributesDidChangeWithFont:(UIFont *)font;
NS_ASSUME_NONNULL_BEGIN
@interface RCTBaseTextViewManager : RCTViewManager
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,57 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import "RCTBaseTextViewManager.h"
@implementation RCTBaseTextViewManager
RCT_EXPORT_MODULE(RCTBaseText)
- (UIView *)view
{
RCTAssert(NO, @"The `-[RCTBaseTextViewManager view]` property must be overridden in subclass.");
return nil;
}
- (RCTShadowView *)shadowView
{
RCTAssert(NO, @"The `-[RCTBaseTextViewManager shadowView]` property must be overridden in subclass.");
return nil;
}
#pragma mark - Text Attributes
// Color
RCT_REMAP_SHADOW_PROPERTY(color, textAttributes.foregroundColor, UIColor)
RCT_REMAP_SHADOW_PROPERTY(backgroundColor, textAttributes.backgroundColor, UIColor)
RCT_REMAP_SHADOW_PROPERTY(opacity, textAttributes.opacity, CGFloat)
// Font
RCT_REMAP_SHADOW_PROPERTY(fontFamily, textAttributes.fontFamily, NSString)
RCT_REMAP_SHADOW_PROPERTY(fontSize, textAttributes.fontSize, CGFloat)
RCT_REMAP_SHADOW_PROPERTY(fontWeight, textAttributes.fontWeight, NSString)
RCT_REMAP_SHADOW_PROPERTY(fontStyle, textAttributes.fontStyle, NSString)
RCT_REMAP_SHADOW_PROPERTY(fontVariant, textAttributes.fontVariant, NSArray)
RCT_REMAP_SHADOW_PROPERTY(allowFontScaling, textAttributes.allowFontScaling, BOOL)
RCT_REMAP_SHADOW_PROPERTY(letterSpacing, textAttributes.letterSpacing, CGFloat)
// Paragraph Styles
RCT_REMAP_SHADOW_PROPERTY(lineHeight, textAttributes.lineHeight, CGFloat)
RCT_REMAP_SHADOW_PROPERTY(textAlign, textAttributes.alignment, NSTextAlignment)
RCT_REMAP_SHADOW_PROPERTY(writingDirection, textAttributes.baseWritingDirection, NSWritingDirection)
// Decoration
RCT_REMAP_SHADOW_PROPERTY(textDecorationColor, textAttributes.textDecorationColor, UIColor)
RCT_REMAP_SHADOW_PROPERTY(textDecorationStyle, textAttributes.textDecorationStyle, NSUnderlineStyle)
RCT_REMAP_SHADOW_PROPERTY(textDecorationLine, textAttributes.textDecorationLine, RCTTextDecorationLineType)
// Shadow
RCT_REMAP_SHADOW_PROPERTY(textShadowOffset, textAttributes.textShadowOffset, CGSize)
RCT_REMAP_SHADOW_PROPERTY(textShadowRadius, textAttributes.textShadowRadius, CGFloat)
RCT_REMAP_SHADOW_PROPERTY(textShadowColor, textAttributes.textShadowColor, UIColor)
// Special
RCT_REMAP_SHADOW_PROPERTY(isHighlighted, textAttributes.isHighlighted, BOOL)
@end
@@ -9,9 +9,13 @@
#import <React/RCTConvert.h>
NS_ASSUME_NONNULL_BEGIN
@interface RCTConvert (Text)
+ (UITextAutocorrectionType)UITextAutocorrectionType:(id)json;
+ (UITextSpellCheckingType)UITextSpellCheckingType:(id)json;
+ (UITextAutocorrectionType)UITextAutocorrectionType:(nullable id)json;
+ (UITextSpellCheckingType)UITextSpellCheckingType:(nullable id)json;
@end
NS_ASSUME_NONNULL_END

This file was deleted.

Oops, something went wrong.

This file was deleted.

Oops, something went wrong.
Oops, something went wrong.

1 comment on commit 2716f53

@filipef101

This comment has been minimized.

filipef101 commented on 2716f53 Mar 13, 2018

@shergin should this be the cause for #18219 ?

Please sign in to comment.