From 397d4666d92f2a71086a3a9e85bd09c4d4617fc8 Mon Sep 17 00:00:00 2001 From: Alex Akers Date: Tue, 7 Apr 2015 02:19:49 -0700 Subject: [PATCH] [React Native] #WIP Modify RCTShadowText measure function to honor maxNumberOfLines property --- Libraries/Text/RCTShadowText.h | 6 ++-- Libraries/Text/RCTShadowText.m | 51 ++++++++++++++++++++++++++++----- Libraries/Text/RCTText.h | 5 ++-- Libraries/Text/RCTText.m | 38 ++++++++++++------------ Libraries/Text/RCTTextManager.m | 21 ++++++-------- 5 files changed, 77 insertions(+), 44 deletions(-) diff --git a/Libraries/Text/RCTShadowText.h b/Libraries/Text/RCTShadowText.h index 82ea2b632..286edb53a 100644 --- a/Libraries/Text/RCTShadowText.h +++ b/Libraries/Text/RCTShadowText.h @@ -23,7 +23,7 @@ extern NSString *const RCTReactTagAttributeName; @property (nonatomic, copy) NSString *fontStyle; @property (nonatomic, assign) BOOL isHighlighted; @property (nonatomic, assign) CGFloat lineHeight; -@property (nonatomic, assign) NSInteger maxNumberOfLines; +@property (nonatomic, assign) NSUInteger maximumNumberOfLines; @property (nonatomic, assign) CGSize shadowOffset; @property (nonatomic, assign) NSTextAlignment textAlign; @@ -31,6 +31,8 @@ extern NSString *const RCTReactTagAttributeName; @property (nonatomic, strong) UIFont *font; @property (nonatomic, assign) NSLineBreakMode truncationMode; -- (NSAttributedString *)attributedString; +@property (nonatomic, copy, readonly) NSAttributedString *attributedString; +@property (nonatomic, strong, readonly) NSLayoutManager *layoutManager; +@property (nonatomic, strong, readonly) NSTextContainer *textContainer; @end diff --git a/Libraries/Text/RCTShadowText.m b/Libraries/Text/RCTShadowText.m index da4b90c06..4201b1b4e 100644 --- a/Libraries/Text/RCTShadowText.m +++ b/Libraries/Text/RCTShadowText.m @@ -20,9 +20,17 @@ NSString *const RCTReactTagAttributeName = @"ReactTagAttributeName"; static css_dim_t RCTMeasure(void *context, float width) { RCTShadowText *shadowText = (__bridge RCTShadowText *)context; - CGSize computedSize = [[shadowText attributedString] boundingRectWithSize:(CGSize){isnan(width) ? CGFLOAT_MAX : width, CGFLOAT_MAX} - options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading - context:nil].size; + + NSTextStorage *textStorage = [[NSTextStorage alloc] initWithAttributedString:[shadowText attributedString]]; + [textStorage addLayoutManager:shadowText.layoutManager]; + + shadowText.textContainer.size = CGSizeMake(isnan(width) ? CGFLOAT_MAX : width, CGFLOAT_MAX); + shadowText.layoutManager.textStorage = textStorage; + [shadowText.layoutManager ensureLayoutForTextContainer:shadowText.textContainer]; + + CGSize computedSize = [shadowText.layoutManager usedRectForTextContainer:shadowText.textContainer].size; + + [textStorage removeLayoutManager:shadowText.layoutManager]; css_dim_t result; result.dimensions[CSS_WIDTH] = RCTCeilPixelValue(computedSize.width); @@ -30,8 +38,9 @@ static css_dim_t RCTMeasure(void *context, float width) return result; } -@implementation RCTShadowText -{ +@implementation RCTShadowText { + NSLayoutManager *_layoutManager; + NSTextContainer *_textContainer; NSAttributedString *_cachedAttributedString; UIFont *_font; } @@ -41,7 +50,15 @@ static css_dim_t RCTMeasure(void *context, float width) if ((self = [super init])) { _fontSize = NAN; _isHighlighted = NO; + + _textContainer = [[NSTextContainer alloc] init]; + _textContainer.lineBreakMode = NSLineBreakByTruncatingTail; + _textContainer.lineFragmentPadding = 0.0; + + _layoutManager = [[NSLayoutManager alloc] init]; + [_layoutManager addTextContainer:_textContainer]; } + return self; } @@ -201,11 +218,31 @@ RCT_TEXT_PROPERTY(FontFamily, _fontFamily, NSString *); RCT_TEXT_PROPERTY(FontSize, _fontSize, CGFloat); RCT_TEXT_PROPERTY(FontWeight, _fontWeight, NSString *); RCT_TEXT_PROPERTY(LineHeight, _lineHeight, CGFloat); -RCT_TEXT_PROPERTY(MaxNumberOfLines, _maxNumberOfLines, NSInteger); RCT_TEXT_PROPERTY(ShadowOffset, _shadowOffset, CGSize); RCT_TEXT_PROPERTY(TextAlign, _textAlign, NSTextAlignment); -RCT_TEXT_PROPERTY(TruncationMode, _truncationMode, NSLineBreakMode); RCT_TEXT_PROPERTY(IsHighlighted, _isHighlighted, BOOL); RCT_TEXT_PROPERTY(Font, _font, UIFont *); +- (NSLineBreakMode)truncationMode +{ + return _textContainer.lineBreakMode; +} + +- (void)setTruncationMode:(NSLineBreakMode)truncationMode +{ + _textContainer.lineBreakMode = truncationMode; + [self dirtyText]; +} + +- (NSUInteger)maximumNumberOfLines +{ + return _textContainer.maximumNumberOfLines; +} + +- (void)setMaximumNumberOfLines:(NSUInteger)maximumNumberOfLines +{ + _textContainer.maximumNumberOfLines = maximumNumberOfLines; + [self dirtyText]; +} + @end diff --git a/Libraries/Text/RCTText.h b/Libraries/Text/RCTText.h index 59b15668a..24b98e991 100644 --- a/Libraries/Text/RCTText.h +++ b/Libraries/Text/RCTText.h @@ -11,9 +11,10 @@ @interface RCTText : UIView +@property (nonatomic, strong) NSLayoutManager *layoutManager; +@property (nonatomic, strong) NSTextContainer *textContainer; @property (nonatomic, copy) NSAttributedString *attributedText; -@property (nonatomic, assign) NSLineBreakMode lineBreakMode; -@property (nonatomic, assign) NSUInteger numberOfLines; + @property (nonatomic, assign) UIEdgeInsets contentInset; @end diff --git a/Libraries/Text/RCTText.m b/Libraries/Text/RCTText.m index a2f7f11cc..c7f00b934 100644 --- a/Libraries/Text/RCTText.m +++ b/Libraries/Text/RCTText.m @@ -23,15 +23,7 @@ - (instancetype)initWithFrame:(CGRect)frame { if ((self = [super initWithFrame:frame])) { - _textContainer = [[NSTextContainer alloc] init]; - _textContainer.lineBreakMode = NSLineBreakByTruncatingTail; - _textContainer.lineFragmentPadding = 0.0; - - _layoutManager = [[NSLayoutManager alloc] init]; - [_layoutManager addTextContainer:_textContainer]; - _textStorage = [[NSTextStorage alloc] init]; - [_textStorage addLayoutManager:_layoutManager]; self.contentMode = UIViewContentModeRedraw; } @@ -50,25 +42,31 @@ [self setNeedsDisplay]; } -- (NSUInteger)numberOfLines +- (void)setTextContainer:(NSTextContainer *)textContainer { - return _textContainer.maximumNumberOfLines; -} + if ([_textContainer isEqual:textContainer]) return; + + _textContainer = textContainer; + + for (NSInteger i = _layoutManager.textContainers.count - 1; i >= 0; i--) { + [_layoutManager removeTextContainerAtIndex:i]; + } + [_layoutManager addTextContainer:_textContainer]; -- (void)setNumberOfLines:(NSUInteger)numberOfLines -{ - _textContainer.maximumNumberOfLines = numberOfLines; [self setNeedsDisplay]; } -- (NSLineBreakMode)lineBreakMode +- (void)setLayoutManager:(NSLayoutManager *)layoutManager { - return _textContainer.lineBreakMode; -} + if ([_layoutManager isEqual:layoutManager]) return; + + _layoutManager = layoutManager; + + for (NSLayoutManager *layoutManager in _textStorage.layoutManagers) { + [_textStorage removeLayoutManager:layoutManager]; + } + [_textStorage addLayoutManager:_layoutManager]; -- (void)setLineBreakMode:(NSLineBreakMode)lineBreakMode -{ - _textContainer.lineBreakMode = lineBreakMode; [self setNeedsDisplay]; } diff --git a/Libraries/Text/RCTTextManager.m b/Libraries/Text/RCTTextManager.m index 8df67e59e..d19dfa71e 100644 --- a/Libraries/Text/RCTTextManager.m +++ b/Libraries/Text/RCTTextManager.m @@ -33,15 +33,6 @@ #pragma mark - View properties RCT_REMAP_VIEW_PROPERTY(containerBackgroundColor, backgroundColor, UIColor) -RCT_CUSTOM_VIEW_PROPERTY(numberOfLines, NSInteger, RCTText) -{ - NSLineBreakMode truncationMode = NSLineBreakByClipping; - view.numberOfLines = json ? [RCTConvert NSInteger:json] : defaultView.numberOfLines; - if (view.numberOfLines > 0) { - truncationMode = NSLineBreakByTruncatingTail; - } - view.lineBreakMode = truncationMode; -} #pragma mark - Shadow properties @@ -65,8 +56,8 @@ RCT_CUSTOM_SHADOW_PROPERTY(containerBackgroundColor, UIColor, RCTShadowText) RCT_CUSTOM_SHADOW_PROPERTY(numberOfLines, NSInteger, RCTShadowText) { NSLineBreakMode truncationMode = NSLineBreakByClipping; - view.maxNumberOfLines = json ? [RCTConvert NSInteger:json] : defaultView.maxNumberOfLines; - if (view.maxNumberOfLines > 0) { + view.maximumNumberOfLines = json ? [RCTConvert NSInteger:json] : defaultView.maximumNumberOfLines; + if (view.maximumNumberOfLines > 0) { truncationMode = NSLineBreakByTruncatingTail; } view.truncationMode = truncationMode; @@ -124,12 +115,16 @@ RCT_CUSTOM_SHADOW_PROPERTY(numberOfLines, NSInteger, RCTShadowText) }; } -- (RCTViewManagerUIBlock)uiBlockToAmendWithShadowView:(RCTShadowView *)shadowView +- (RCTViewManagerUIBlock)uiBlockToAmendWithShadowView:(RCTShadowText *)shadowView { NSNumber *reactTag = shadowView.reactTag; UIEdgeInsets padding = shadowView.paddingAsInsets; + return ^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) { - ((RCTText *)viewRegistry[reactTag]).contentInset = padding; + RCTText *text = (RCTText *)viewRegistry[reactTag]; + text.contentInset = padding; + text.layoutManager = shadowView.layoutManager; + text.textContainer = shadowView.textContainer; }; }