Fixed text update on OSS
This commit is contained in:
parent
a2db4a4a5b
commit
49e87af934
|
@ -28,7 +28,6 @@ extern NSString *const RCTReactTagAttributeName;
|
|||
@property (nonatomic, strong) UIColor *textBackgroundColor;
|
||||
@property (nonatomic, assign) NSWritingDirection writingDirection;
|
||||
|
||||
- (NSTextStorage *)buildTextStorageForWidth:(CGFloat)width;
|
||||
- (void)recomputeText;
|
||||
|
||||
@end
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
#import "RCTConvert.h"
|
||||
#import "RCTLog.h"
|
||||
#import "RCTShadowRawText.h"
|
||||
#import "RCTSparseArray.h"
|
||||
#import "RCTText.h"
|
||||
#import "RCTUtils.h"
|
||||
|
||||
NSString *const RCTIsHighlightedAttributeName = @"IsHighlightedAttributeName";
|
||||
|
@ -19,6 +21,8 @@ NSString *const RCTReactTagAttributeName = @"ReactTagAttributeName";
|
|||
|
||||
@implementation RCTShadowText
|
||||
{
|
||||
NSTextStorage *_cachedTextStorage;
|
||||
CGFloat _cachedTextStorageWidth;
|
||||
NSAttributedString *_cachedAttributedString;
|
||||
CGFloat _effectiveLetterSpacing;
|
||||
}
|
||||
|
@ -50,8 +54,35 @@ static css_dim_t RCTMeasure(void *context, float width)
|
|||
return self;
|
||||
}
|
||||
|
||||
- (NSDictionary *)processUpdatedProperties:(NSMutableSet *)applierBlocks
|
||||
parentProperties:(NSDictionary *)parentProperties
|
||||
{
|
||||
parentProperties = [super processUpdatedProperties:applierBlocks
|
||||
parentProperties:parentProperties];
|
||||
|
||||
NSTextStorage *textStorage = [self buildTextStorageForWidth:self.frame.size.width];
|
||||
[applierBlocks addObject:^(RCTSparseArray *viewRegistry) {
|
||||
RCTText *view = viewRegistry[self.reactTag];
|
||||
view.textStorage = textStorage;
|
||||
}];
|
||||
|
||||
return parentProperties;
|
||||
}
|
||||
|
||||
- (void)applyLayoutNode:(css_node_t *)node
|
||||
viewsWithNewFrame:(NSMutableSet *)viewsWithNewFrame
|
||||
absolutePosition:(CGPoint)absolutePosition
|
||||
{
|
||||
[super applyLayoutNode:node viewsWithNewFrame:viewsWithNewFrame absolutePosition:absolutePosition];
|
||||
[self dirtyPropagation];
|
||||
}
|
||||
|
||||
- (NSTextStorage *)buildTextStorageForWidth:(CGFloat)width
|
||||
{
|
||||
if (_cachedTextStorage && width == _cachedTextStorageWidth) {
|
||||
return _cachedTextStorage;
|
||||
}
|
||||
|
||||
NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init];
|
||||
|
||||
NSTextStorage *textStorage = [[NSTextStorage alloc] initWithAttributedString:self.attributedString];
|
||||
|
@ -69,13 +100,23 @@ static css_dim_t RCTMeasure(void *context, float width)
|
|||
[layoutManager addTextContainer:textContainer];
|
||||
[layoutManager ensureLayoutForTextContainer:textContainer];
|
||||
|
||||
_cachedTextStorage = textStorage;
|
||||
_cachedTextStorageWidth = width;
|
||||
|
||||
return textStorage;
|
||||
}
|
||||
|
||||
- (void)dirtyText
|
||||
{
|
||||
[super dirtyText];
|
||||
_cachedTextStorage = nil;
|
||||
}
|
||||
|
||||
- (void)recomputeText
|
||||
{
|
||||
[self attributedString];
|
||||
[self setTextComputed];
|
||||
[self dirtyPropagation];
|
||||
}
|
||||
|
||||
- (NSAttributedString *)attributedString
|
||||
|
|
|
@ -96,12 +96,10 @@ RCT_EXPORT_SHADOW_PROPERTY(numberOfLines, NSUInteger)
|
|||
{
|
||||
NSNumber *reactTag = shadowView.reactTag;
|
||||
UIEdgeInsets padding = shadowView.paddingAsInsets;
|
||||
NSTextStorage *textStorage = [shadowView buildTextStorageForWidth:shadowView.frame.size.width];
|
||||
|
||||
return ^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {
|
||||
RCTText *text = viewRegistry[reactTag];
|
||||
text.contentInset = padding;
|
||||
text.textStorage = textStorage;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -481,6 +481,10 @@ static NSDictionary *RCTViewConfigForModule(Class managerClass, NSString *viewNa
|
|||
shadowView.newView = NO;
|
||||
}
|
||||
|
||||
// These are blocks to be executed on each view, immediately after
|
||||
// reactSetFrame: has been called. Note that if reactSetFrame: is not called,
|
||||
// these won't be called either, so this is not a suitable place to update
|
||||
// properties that aren't related to layout.
|
||||
NSMutableArray *updateBlocks = [[NSMutableArray alloc] init];
|
||||
for (RCTShadowView *shadowView in viewsWithNewFrames) {
|
||||
RCTViewManager *manager = _viewManagerRegistry[shadowView.reactTag];
|
||||
|
|
|
@ -20,8 +20,7 @@ typedef NS_ENUM(NSUInteger, RCTUpdateLifecycle) {
|
|||
RCTUpdateLifecycleDirtied,
|
||||
};
|
||||
|
||||
// TODO: is this redundact now?
|
||||
typedef void (^RCTApplierBlock)(RCTSparseArray *);
|
||||
typedef void (^RCTApplierBlock)(RCTSparseArray *viewRegistry);
|
||||
|
||||
/**
|
||||
* ShadowView tree mirrors RCT view tree. Every node is highly stateful.
|
||||
|
@ -117,34 +116,48 @@ typedef void (^RCTApplierBlock)(RCTSparseArray *);
|
|||
* The applierBlocks set contains RCTApplierBlock functions that must be applied
|
||||
* on the main thread in order to update the view.
|
||||
*/
|
||||
- (void)collectUpdatedProperties:(NSMutableSet *)applierBlocks parentProperties:(NSDictionary *)parentProperties;
|
||||
- (void)collectUpdatedProperties:(NSMutableSet *)applierBlocks
|
||||
parentProperties:(NSDictionary *)parentProperties;
|
||||
|
||||
/**
|
||||
* Process the updated properties and apply them to view. Shadow view classes
|
||||
* that add additional propagating properties should override this method.
|
||||
*/
|
||||
- (NSDictionary *)processUpdatedProperties:(NSMutableSet *)applierBlocks
|
||||
parentProperties:(NSDictionary *)parentProperties NS_REQUIRES_SUPER;
|
||||
|
||||
/**
|
||||
* Calculate all views whose frame needs updating after layout has been calculated.
|
||||
* The viewsWithNewFrame set contains the reactTags of the views that need updating.
|
||||
*/
|
||||
- (void)collectRootUpdatedFrames:(NSMutableSet *)viewsWithNewFrame parentConstraint:(CGSize)parentConstraint;
|
||||
- (void)collectRootUpdatedFrames:(NSMutableSet *)viewsWithNewFrame
|
||||
parentConstraint:(CGSize)parentConstraint;
|
||||
|
||||
/**
|
||||
* Recursively apply layout to children.
|
||||
*/
|
||||
- (void)applyLayoutNode:(css_node_t *)node
|
||||
viewsWithNewFrame:(NSMutableSet *)viewsWithNewFrame
|
||||
absolutePosition:(CGPoint)absolutePosition NS_REQUIRES_SUPER;
|
||||
|
||||
/**
|
||||
* The following are implementation details exposed to subclasses. Do not call them directly
|
||||
*/
|
||||
- (void)fillCSSNode:(css_node_t *)node;
|
||||
- (void)dirtyLayout;
|
||||
- (void)fillCSSNode:(css_node_t *)node NS_REQUIRES_SUPER;
|
||||
- (void)dirtyLayout NS_REQUIRES_SUPER;
|
||||
- (BOOL)isLayoutDirty;
|
||||
|
||||
// TODO: is this still needed?
|
||||
- (void)dirtyPropagation;
|
||||
- (void)dirtyPropagation NS_REQUIRES_SUPER;
|
||||
- (BOOL)isPropagationDirty;
|
||||
|
||||
// TODO: move this to text node?
|
||||
- (void)dirtyText;
|
||||
- (void)dirtyText NS_REQUIRES_SUPER;
|
||||
- (void)setTextComputed NS_REQUIRES_SUPER;
|
||||
- (BOOL)isTextDirty;
|
||||
- (void)setTextComputed;
|
||||
|
||||
/**
|
||||
* Triggers a recalculation of the shadow view's layout.
|
||||
*/
|
||||
- (void)updateLayout;
|
||||
- (void)updateLayout NS_REQUIRES_SUPER;
|
||||
|
||||
/**
|
||||
* Computes the recursive offset, meaning the sum of all descendant offsets -
|
||||
|
|
|
@ -120,7 +120,9 @@ static void RCTProcessMetaProps(const float metaProps[META_PROP_COUNT], float st
|
|||
// width = 213.5 - 106.5 = 107
|
||||
// You'll notice that this is the same width we calculated for the parent view because we've taken its position into account.
|
||||
|
||||
- (void)applyLayoutNode:(css_node_t *)node viewsWithNewFrame:(NSMutableSet *)viewsWithNewFrame absolutePosition:(CGPoint)absolutePosition
|
||||
- (void)applyLayoutNode:(css_node_t *)node
|
||||
viewsWithNewFrame:(NSMutableSet *)viewsWithNewFrame
|
||||
absolutePosition:(CGPoint)absolutePosition
|
||||
{
|
||||
if (!node->layout.should_update) {
|
||||
return;
|
||||
|
@ -161,12 +163,19 @@ static void RCTProcessMetaProps(const float metaProps[META_PROP_COUNT], float st
|
|||
|
||||
for (int i = 0; i < node->children_count; ++i) {
|
||||
RCTShadowView *child = (RCTShadowView *)_reactSubviews[i];
|
||||
[child applyLayoutNode:node->get_child(node->context, i) viewsWithNewFrame:viewsWithNewFrame absolutePosition:absolutePosition];
|
||||
[child applyLayoutNode:node->get_child(node->context, i)
|
||||
viewsWithNewFrame:viewsWithNewFrame
|
||||
absolutePosition:absolutePosition];
|
||||
}
|
||||
}
|
||||
|
||||
- (NSDictionary *)processBackgroundColor:(NSMutableSet *)applierBlocks parentProperties:(NSDictionary *)parentProperties
|
||||
- (NSDictionary *)processUpdatedProperties:(NSMutableSet *)applierBlocks
|
||||
parentProperties:(NSDictionary *)parentProperties
|
||||
{
|
||||
// TODO: we always refresh all propagated properties when propagation is
|
||||
// dirtied, but really we should track which properties have changed and
|
||||
// only update those.
|
||||
|
||||
if (!_backgroundColor) {
|
||||
UIColor *parentBackgroundColor = parentProperties[RCTBackgroundColorProp];
|
||||
if (parentBackgroundColor) {
|
||||
|
@ -190,14 +199,15 @@ static void RCTProcessMetaProps(const float metaProps[META_PROP_COUNT], float st
|
|||
return parentProperties;
|
||||
}
|
||||
|
||||
- (void)collectUpdatedProperties:(NSMutableSet *)applierBlocks parentProperties:(NSDictionary *)parentProperties
|
||||
- (void)collectUpdatedProperties:(NSMutableSet *)applierBlocks
|
||||
parentProperties:(NSDictionary *)parentProperties
|
||||
{
|
||||
if (_propagationLifecycle == RCTUpdateLifecycleComputed && [parentProperties isEqualToDictionary:_lastParentProperties]) {
|
||||
return;
|
||||
}
|
||||
_propagationLifecycle = RCTUpdateLifecycleComputed;
|
||||
_lastParentProperties = parentProperties;
|
||||
NSDictionary *nextProps = [self processBackgroundColor:applierBlocks parentProperties:parentProperties];
|
||||
NSDictionary *nextProps = [self processUpdatedProperties:applierBlocks parentProperties:parentProperties];
|
||||
for (RCTShadowView *child in _reactSubviews) {
|
||||
[child collectUpdatedProperties:applierBlocks parentProperties:nextProps];
|
||||
}
|
||||
|
@ -212,21 +222,19 @@ static void RCTProcessMetaProps(const float metaProps[META_PROP_COUNT], float st
|
|||
|
||||
- (CGRect)measureLayoutRelativeToAncestor:(RCTShadowView *)ancestor
|
||||
{
|
||||
CGFloat totalOffsetTop = 0.0;
|
||||
CGFloat totalOffsetLeft = 0.0;
|
||||
CGSize size = self.frame.size;
|
||||
CGPoint offset = CGPointZero;
|
||||
NSInteger depth = 30; // max depth to search
|
||||
RCTShadowView *shadowView = self;
|
||||
while (depth && shadowView && shadowView != ancestor) {
|
||||
totalOffsetTop += shadowView.frame.origin.y;
|
||||
totalOffsetLeft += shadowView.frame.origin.x;
|
||||
offset.x += shadowView.frame.origin.x;
|
||||
offset.y += shadowView.frame.origin.y;
|
||||
shadowView = shadowView->_superview;
|
||||
depth--;
|
||||
}
|
||||
if (ancestor != shadowView) {
|
||||
return CGRectNull;
|
||||
}
|
||||
return (CGRect){{totalOffsetLeft, totalOffsetTop}, size};
|
||||
return (CGRect){offset, self.frame.size};
|
||||
}
|
||||
|
||||
- (instancetype)init
|
||||
|
|
Loading…
Reference in New Issue