/** * 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 { NSTextStorage *_textStorage; NSMutableArray *_reactSubviews; } - (instancetype)initWithFrame:(CGRect)frame { if ((self = [super initWithFrame:frame])) { _textStorage = [[NSTextStorage alloc] init]; _reactSubviews = [NSMutableArray array]; self.isAccessibilityElement = YES; self.accessibilityTraits |= UIAccessibilityTraitStaticText; self.opaque = NO; self.contentMode = UIViewContentModeRedraw; } return self; } - (void)reactSetFrame:(CGRect)frame { // Text looks super weird if its frame is animated. // This disables the frame animation, without affecting opacity, etc. [UIView performWithoutAnimation:^{ [super reactSetFrame:frame]; }]; } - (void)insertReactSubview:(UIView *)subview atIndex:(NSInteger)atIndex { [_reactSubviews insertObject:subview atIndex:atIndex]; } - (void)removeReactSubview:(UIView *)subview { [_reactSubviews removeObject:subview]; } - (NSMutableArray *)reactSubviews { return _reactSubviews; } - (void)setTextStorage:(NSTextStorage *)textStorage { _textStorage = textStorage; [self setNeedsDisplay]; } - (void)drawRect:(CGRect)rect { NSLayoutManager *layoutManager = [_textStorage.layoutManagers firstObject]; NSTextContainer *textContainer = [layoutManager.textContainers firstObject]; CGRect textFrame = UIEdgeInsetsInsetRect(self.bounds, _contentInset); NSRange glyphRange = [layoutManager glyphRangeForTextContainer:textContainer]; [layoutManager drawBackgroundForGlyphRange:glyphRange atPoint:textFrame.origin]; [layoutManager drawGlyphsForGlyphRange:glyphRange atPoint:textFrame.origin]; } - (NSNumber *)reactTagAtPoint:(CGPoint)point { NSNumber *reactTag = self.reactTag; CGFloat fraction; NSLayoutManager *layoutManager = [_textStorage.layoutManagers firstObject]; NSTextContainer *textContainer = [layoutManager.textContainers firstObject]; NSUInteger characterIndex = [layoutManager characterIndexForPoint:point inTextContainer:textContainer fractionOfDistanceBetweenInsertionPoints:&fraction]; // 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; } #pragma mark - Accessibility - (NSString *)accessibilityLabel { return _textStorage.string; } @end