RCTBaseTextShadowView.m 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. /*
  2. * Copyright (c) Facebook, Inc. and its affiliates.
  3. *
  4. * This source code is licensed under the MIT license found in the
  5. * LICENSE file in the root directory of this source tree.
  6. */
  7. #import <React/RCTBaseTextShadowView.h>
  8. #import <React/RCTShadowView+Layout.h>
  9. #import <React/RCTRawTextShadowView.h>
  10. #import <React/RCTVirtualTextShadowView.h>
  11. NSString *const RCTBaseTextShadowViewEmbeddedShadowViewAttributeName = @"RCTBaseTextShadowViewEmbeddedShadowViewAttributeName";
  12. static void RCTInlineViewYogaNodeDirtied(YGNodeRef node)
  13. {
  14. // An inline view (a view nested inside of a text node) does not have a parent
  15. // in the Yoga tree. Consequently, we have to manually propagate the inline
  16. // view's dirty signal up through the text nodes. At some point, it'll reach
  17. // the outermost text node which has a Yoga node and then Yoga will take over
  18. // the dirty signal propagation.
  19. RCTShadowView *inlineView = (__bridge RCTShadowView *)YGNodeGetContext(node);
  20. RCTBaseTextShadowView *baseTextShadowView =
  21. (RCTBaseTextShadowView *)inlineView.reactSuperview;
  22. [baseTextShadowView dirtyLayout];
  23. }
  24. @implementation RCTBaseTextShadowView
  25. - (instancetype)init
  26. {
  27. if (self = [super init]) {
  28. _textAttributes = [RCTTextAttributes new];
  29. }
  30. return self;
  31. }
  32. - (void)setReactTag:(NSNumber *)reactTag
  33. {
  34. [super setReactTag:reactTag];
  35. _textAttributes.tag = reactTag;
  36. }
  37. #pragma mark - Life Cycle
  38. - (void)insertReactSubview:(RCTShadowView *)subview atIndex:(NSInteger)index
  39. {
  40. [super insertReactSubview:subview atIndex:index];
  41. [self dirtyLayout];
  42. if (![subview isKindOfClass:[RCTVirtualTextShadowView class]]) {
  43. YGNodeSetDirtiedFunc(subview.yogaNode, RCTInlineViewYogaNodeDirtied);
  44. }
  45. }
  46. - (void)removeReactSubview:(RCTShadowView *)subview
  47. {
  48. if (![subview isKindOfClass:[RCTVirtualTextShadowView class]]) {
  49. YGNodeSetDirtiedFunc(subview.yogaNode, NULL);
  50. }
  51. [self dirtyLayout];
  52. [super removeReactSubview:subview];
  53. }
  54. #pragma mark - attributedString
  55. - (NSAttributedString *)attributedTextWithBaseTextAttributes:(nullable RCTTextAttributes *)baseTextAttributes
  56. {
  57. RCTTextAttributes *textAttributes;
  58. if (baseTextAttributes) {
  59. textAttributes = [baseTextAttributes copy];
  60. [textAttributes applyTextAttributes:self.textAttributes];
  61. } else {
  62. textAttributes = [self.textAttributes copy];
  63. }
  64. if (cachedAttributedText && [cachedTextAttributes isEqual:textAttributes]) {
  65. return cachedAttributedText;
  66. }
  67. NSMutableAttributedString *attributedText = [NSMutableAttributedString new];
  68. [attributedText beginEditing];
  69. for (RCTShadowView *shadowView in self.reactSubviews) {
  70. // Special Case: RCTRawTextShadowView
  71. if ([shadowView isKindOfClass:[RCTRawTextShadowView class]]) {
  72. RCTRawTextShadowView *rawTextShadowView = (RCTRawTextShadowView *)shadowView;
  73. NSString *text = rawTextShadowView.text;
  74. if (text) {
  75. NSAttributedString *rawTextAttributedString =
  76. [[NSAttributedString alloc] initWithString:[textAttributes applyTextAttributesToText:text]
  77. attributes:textAttributes.effectiveTextAttributes];
  78. [attributedText appendAttributedString:rawTextAttributedString];
  79. }
  80. continue;
  81. }
  82. // Special Case: RCTBaseTextShadowView
  83. if ([shadowView isKindOfClass:[RCTBaseTextShadowView class]]) {
  84. RCTBaseTextShadowView *baseTextShadowView = (RCTBaseTextShadowView *)shadowView;
  85. NSAttributedString *baseTextAttributedString =
  86. [baseTextShadowView attributedTextWithBaseTextAttributes:textAttributes];
  87. [attributedText appendAttributedString:baseTextAttributedString];
  88. continue;
  89. }
  90. // Generic Case: Any RCTShadowView
  91. NSTextAttachment *attachment = [NSTextAttachment new];
  92. NSMutableAttributedString *embeddedShadowViewAttributedString = [NSMutableAttributedString new];
  93. [embeddedShadowViewAttributedString beginEditing];
  94. [embeddedShadowViewAttributedString appendAttributedString:[NSAttributedString attributedStringWithAttachment:attachment]];
  95. [embeddedShadowViewAttributedString addAttribute:RCTBaseTextShadowViewEmbeddedShadowViewAttributeName
  96. value:shadowView
  97. range:(NSRange){0, embeddedShadowViewAttributedString.length}];
  98. [embeddedShadowViewAttributedString endEditing];
  99. [attributedText appendAttributedString:embeddedShadowViewAttributedString];
  100. }
  101. [attributedText endEditing];
  102. [self clearLayout];
  103. cachedAttributedText = [attributedText copy];
  104. cachedTextAttributes = textAttributes;
  105. return cachedAttributedText;
  106. }
  107. - (void)dirtyLayout
  108. {
  109. [super dirtyLayout];
  110. cachedAttributedText = nil;
  111. cachedTextAttributes = nil;
  112. }
  113. - (void)didUpdateReactSubviews
  114. {
  115. [super didUpdateReactSubviews];
  116. [self dirtyLayout];
  117. }
  118. - (void)didSetProps:(NSArray<NSString *> *)changedProps
  119. {
  120. [super didSetProps:changedProps];
  121. [self dirtyLayout];
  122. }
  123. @end