From 96708d58e4b413032f4e5ffcc71e8da67ef99ea0 Mon Sep 17 00:00:00 2001 From: Jiayan Zhuang Date: Mon, 6 Jul 2020 14:58:38 -0700 Subject: [PATCH] Add getRectWithAttributedString() to RCTTextLayoutManager Summary: Changelog: [iOS][Added] - `getRectWithAttributedString()` aims to get the rect of the fragment with embedded link, which is necessary when building the `accessibilityElement`. In this function, we first enumerate attributedString to find the range of fragments whose `accessibilityRole` is @"link". Then we calculate the rect of the fragment and send to the block and we would define what to do in the block in `RCTParagraphComponentAccessibilityProvider`. Reviewed By: shergin Differential Revision: D22286733 fbshipit-source-id: 4d11cb54375a4e9e72869e646fcb484de089b14b --- .../platform/ios/RCTTextLayoutManager.h | 13 ++++++ .../platform/ios/RCTTextLayoutManager.mm | 40 +++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/ReactCommon/fabric/textlayoutmanager/platform/ios/RCTTextLayoutManager.h b/ReactCommon/fabric/textlayoutmanager/platform/ios/RCTTextLayoutManager.h index 0e10ce2a085..eb495776e8c 100644 --- a/ReactCommon/fabric/textlayoutmanager/platform/ios/RCTTextLayoutManager.h +++ b/ReactCommon/fabric/textlayoutmanager/platform/ios/RCTTextLayoutManager.h @@ -15,6 +15,13 @@ NS_ASSUME_NONNULL_BEGIN +/** + @abstract Enumeration block for text fragments. +*/ + +using RCTTextLayoutFragmentEnumerationBlock = + void (^)(CGRect fragmentRect, NSString *_Nonnull fragmentText, NSString *value); + /** * iOS-specific TextLayoutManager */ @@ -38,6 +45,12 @@ NS_ASSUME_NONNULL_BEGIN frame:(CGRect)frame atPoint:(CGPoint)point; +- (void)getRectWithAttributedString:(facebook::react::AttributedString)attributedString + paragraphAttributes:(facebook::react::ParagraphAttributes)paragraphAttributes + enumerateAttribute:(NSString *)enumerateAttribute + frame:(CGRect)frame + usingBlock:(RCTTextLayoutFragmentEnumerationBlock)block; + @end NS_ASSUME_NONNULL_END diff --git a/ReactCommon/fabric/textlayoutmanager/platform/ios/RCTTextLayoutManager.mm b/ReactCommon/fabric/textlayoutmanager/platform/ios/RCTTextLayoutManager.mm index 7e0c4a9231a..de6e354aa55 100644 --- a/ReactCommon/fabric/textlayoutmanager/platform/ios/RCTTextLayoutManager.mm +++ b/ReactCommon/fabric/textlayoutmanager/platform/ios/RCTTextLayoutManager.mm @@ -182,4 +182,44 @@ static NSLineBreakMode RCTNSLineBreakModeFromEllipsizeMode(EllipsizeMode ellipsi return unwrapManagedObject(sharedNSAttributedString); } +- (void)getRectWithAttributedString:(AttributedString)attributedString + paragraphAttributes:(ParagraphAttributes)paragraphAttributes + enumerateAttribute:(NSString *)enumerateAttribute + frame:(CGRect)frame + usingBlock:(RCTTextLayoutFragmentEnumerationBlock)block +{ + NSTextStorage *textStorage = [self + _textStorageAndLayoutManagerWithAttributesString:[self _nsAttributedStringFromAttributedString:attributedString] + paragraphAttributes:paragraphAttributes + size:frame.size]; + + NSLayoutManager *layoutManager = textStorage.layoutManagers.firstObject; + NSTextContainer *textContainer = layoutManager.textContainers.firstObject; + [layoutManager ensureLayoutForTextContainer:textContainer]; + + NSRange glyphRange = [layoutManager glyphRangeForTextContainer:textContainer]; + NSRange characterRange = [layoutManager characterRangeForGlyphRange:glyphRange actualGlyphRange:NULL]; + + [textStorage enumerateAttribute:enumerateAttribute + inRange:characterRange + options:0 + usingBlock:^(NSString *value, NSRange range, BOOL *pause) { + if (!value) { + return; + } + + [layoutManager + enumerateEnclosingRectsForGlyphRange:range + withinSelectedGlyphRange:range + inTextContainer:textContainer + usingBlock:^(CGRect enclosingRect, BOOL *_Nonnull stop) { + block( + enclosingRect, + [textStorage attributedSubstringFromRange:range].string, + value); + *stop = YES; + }]; + }]; +} + @end