react-native/ReactKit/Views/RCTText.m

101 lines
2.6 KiB
Objective-C

// Copyright 2004-present Facebook. All Rights Reserved.
#import "RCTText.h"
#import "RCTShadowText.h"
#import "RCTUtils.h"
#import "UIView+ReactKit.h"
@implementation RCTText
{
NSLayoutManager *_layoutManager;
NSTextStorage *_textStorage;
NSTextContainer *_textContainer;
}
- (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;
}
return self;
}
- (NSAttributedString *)attributedText
{
return [_textStorage copy];
}
- (void)setAttributedText:(NSAttributedString *)attributedText
{
[_textStorage setAttributedString:attributedText];
[self setNeedsDisplay];
}
- (NSUInteger)numberOfLines
{
return _textContainer.maximumNumberOfLines;
}
- (void)setNumberOfLines:(NSUInteger)numberOfLines
{
_textContainer.maximumNumberOfLines = numberOfLines;
[self setNeedsDisplay];
}
- (NSLineBreakMode)lineBreakMode
{
return _textContainer.lineBreakMode;
}
- (void)setLineBreakMode:(NSLineBreakMode)lineBreakMode
{
_textContainer.lineBreakMode = lineBreakMode;
[self setNeedsDisplay];
}
- (void)layoutSubviews
{
[super layoutSubviews];
// The header comment for `size` says that a height of 0.0 should be enough,
// but it isn't.
_textContainer.size = CGSizeMake(self.bounds.size.width, CGFLOAT_MAX);
}
- (void)drawRect:(CGRect)rect
{
NSRange glyphRange = [_layoutManager glyphRangeForTextContainer:_textContainer];
[_layoutManager drawBackgroundForGlyphRange:glyphRange atPoint:CGPointZero];
[_layoutManager drawGlyphsForGlyphRange:glyphRange atPoint:CGPointZero];
}
- (NSNumber *)reactTagAtPoint:(CGPoint)point
{
CGFloat fraction;
NSUInteger characterIndex = [_layoutManager characterIndexForPoint:point inTextContainer:_textContainer fractionOfDistanceBetweenInsertionPoints:&fraction];
NSNumber *reactTag = nil;
// If the point is not before (fraction == 0.0) the first character and not
// after (fraction == 1.0) the last character, then the attribute is valid.
if (_textStorage.length > 0 && (fraction > 0 || characterIndex > 0) && (fraction < 1 || characterIndex < _textStorage.length - 1)) {
reactTag = [_textStorage attribute:RCTReactTagAttributeName atIndex:characterIndex effectiveRange:NULL];
}
return reactTag ?: self.reactTag;
}
@end