react-native/Libraries/Text/RCTText.m

112 lines
2.9 KiB
Objective-C

/**
* 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 "RCTText.h"
#import "RCTShadowText.h"
#import "RCTUtils.h"
#import "UIView+React.h"
@implementation RCTText
{
NSLayoutManager *_layoutManager;
NSTextStorage *_textStorage;
NSTextContainer *_textContainer;
}
- (instancetype)initWithFrame:(CGRect)frame
{
if ((self = [super initWithFrame:frame])) {
_textStorage = [[NSTextStorage alloc] init];
self.contentMode = UIViewContentModeRedraw;
}
return self;
}
- (NSAttributedString *)attributedText
{
return [_textStorage copy];
}
- (void)setAttributedText:(NSAttributedString *)attributedText
{
[_textStorage setAttributedString:attributedText];
[self setNeedsDisplay];
}
- (void)setTextContainer:(NSTextContainer *)textContainer
{
if ([_textContainer isEqual:textContainer]) return;
_textContainer = textContainer;
for (NSInteger i = _layoutManager.textContainers.count - 1; i >= 0; i--) {
[_layoutManager removeTextContainerAtIndex:i];
}
[_layoutManager addTextContainer:_textContainer];
[self setNeedsDisplay];
}
- (void)setLayoutManager:(NSLayoutManager *)layoutManager
{
if ([_layoutManager isEqual:layoutManager]) return;
_layoutManager = layoutManager;
for (NSLayoutManager *layoutManager in _textStorage.layoutManagers) {
[_textStorage removeLayoutManager:layoutManager];
}
[_textStorage addLayoutManager:_layoutManager];
[self setNeedsDisplay];
}
- (CGRect)textFrame
{
return UIEdgeInsetsInsetRect(self.bounds, _contentInset);
}
- (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 textFrame].size.width, CGFLOAT_MAX);
}
- (void)drawRect:(CGRect)rect
{
CGPoint origin = [self textFrame].origin;
NSRange glyphRange = [_layoutManager glyphRangeForTextContainer:_textContainer];
[_layoutManager drawBackgroundForGlyphRange:glyphRange atPoint:origin];
[_layoutManager drawGlyphsForGlyphRange:glyphRange atPoint:origin];
}
- (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