Add support for basic ASTextNode2 link highlighting (#1518)

* Add support for basic ASTextNode2 link highlighting

* Add approach to fix line highlighting

* Enable proper highlighting for first row

* Fix snapshot highlighting test for ASTextNode2

* Update screenshot again

* Address comments
This commit is contained in:
Michael Schneider
2019-06-03 08:57:31 -07:00
committed by Adlai Holler
parent 1b4b6a3f02
commit ab8ea069ea
4 changed files with 113 additions and 6 deletions
+1 -1
View File
@@ -766,6 +766,7 @@ static NSArray *DefaultLinkAttributeNames() {
- (void)_setHighlightRange:(NSRange)highlightRange forAttributeName:(NSString *)highlightedAttributeName value:(id)highlightedAttributeValue animated:(BOOL)animated
{
ASDisplayNodeAssertMainThread();
ASLockScopeSelf();
_highlightedLinkAttributeName = highlightedAttributeName;
_highlightedLinkAttributeValue = highlightedAttributeValue;
@@ -825,7 +826,6 @@ static NSArray *DefaultLinkAttributeNames() {
}
if (highlightTargetLayer != nil) {
ASLockScopeSelf();
ASTextKitRenderer *renderer = [self _locked_renderer];
NSArray *highlightRects = [renderer rectsForTextRange:highlightRange measureOption:ASTextKitRendererMeasureOptionBlock];
+109 -4
View File
@@ -137,6 +137,8 @@ static NS_RETURNS_RETAINED ASTextLayout *ASTextNodeCompatibleLayoutWithContainer
return layout;
}
static const NSTimeInterval ASTextNodeHighlightFadeOutDuration = 0.15;
static const NSTimeInterval ASTextNodeHighlightFadeInDuration = 0.1;
static const CGFloat ASTextNodeHighlightLightOpacity = 0.11;
static const CGFloat ASTextNodeHighlightDarkOpacity = 0.22;
static NSString *ASTextNodeTruncationTokenAttributeName = @"ASTextNodeTruncationAttribute";
@@ -778,17 +780,114 @@ static NSArray *DefaultLinkAttributeNames() {
- (void)_setHighlightRange:(NSRange)highlightRange forAttributeName:(NSString *)highlightedAttributeName value:(id)highlightedAttributeValue animated:(BOOL)animated
{
ASDisplayNodeAssertMainThread();
ASLockScopeSelf(); // Protect usage of _highlight* ivars.
// Set these so that link tapping works.
_highlightedLinkAttributeName = highlightedAttributeName;
_highlightedLinkAttributeValue = highlightedAttributeValue;
_highlightRange = highlightRange;
AS_TEXT_ALERT_UNIMPLEMENTED_FEATURE();
// Much of the code from original ASTextNode is probably usable here.
if (!NSEqualRanges(highlightRange, _highlightRange) && ((0 != highlightRange.length) || (0 != _highlightRange.length))) {
return;
_highlightRange = highlightRange;
if (_activeHighlightLayer) {
if (animated) {
__weak CALayer *weakHighlightLayer = _activeHighlightLayer;
_activeHighlightLayer = nil;
weakHighlightLayer.opacity = 0.0;
CFTimeInterval beginTime = CACurrentMediaTime();
CABasicAnimation *possibleFadeIn = (CABasicAnimation *)[weakHighlightLayer animationForKey:@"opacity"];
if (possibleFadeIn) {
// Calculate when we should begin fading out based on the end of the fade in animation,
// Also check to make sure that the new begin time hasn't already passed
CGFloat newBeginTime = (possibleFadeIn.beginTime + possibleFadeIn.duration);
if (newBeginTime > beginTime) {
beginTime = newBeginTime;
}
}
CABasicAnimation *fadeOut = [CABasicAnimation animationWithKeyPath:@"opacity"];
fadeOut.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
fadeOut.fromValue = possibleFadeIn.toValue ? : @(((CALayer *)weakHighlightLayer.presentationLayer).opacity);
fadeOut.toValue = @0.0;
fadeOut.fillMode = kCAFillModeBoth;
fadeOut.duration = ASTextNodeHighlightFadeOutDuration;
fadeOut.beginTime = beginTime;
dispatch_block_t prev = [CATransaction completionBlock];
[CATransaction setCompletionBlock:^{
[weakHighlightLayer removeFromSuperlayer];
}];
[weakHighlightLayer addAnimation:fadeOut forKey:fadeOut.keyPath];
[CATransaction setCompletionBlock:prev];
} else {
[_activeHighlightLayer removeFromSuperlayer];
_activeHighlightLayer = nil;
}
}
if (0 != highlightRange.length) {
// Find layer in hierarchy that allows us to draw highlighting on.
CALayer *highlightTargetLayer = self.layer;
while (highlightTargetLayer != nil) {
if (highlightTargetLayer.as_allowsHighlightDrawing) {
break;
}
highlightTargetLayer = highlightTargetLayer.superlayer;
}
if (highlightTargetLayer != nil) {
// TODO: The copy and application of size shouldn't be required, but it is currently.
// See discussion in https://github.com/TextureGroup/Texture/pull/396
ASTextContainer *textContainerCopy = [_textContainer copy];
textContainerCopy.size = self.calculatedSize;
ASTextLayout *layout = ASTextNodeCompatibleLayoutWithContainerAndText(textContainerCopy, _attributedText);
NSArray<ASTextSelectionRect *> *highlightRects = [layout selectionRectsWithoutStartAndEndForRange:[ASTextRange rangeWithRange:highlightRange]];
NSMutableArray *converted = [NSMutableArray arrayWithCapacity:highlightRects.count];
CALayer *layer = self.layer;
UIEdgeInsets shadowPadding = self.shadowPadding;
for (ASTextSelectionRect *rectValue in highlightRects) {
// Adjust shadow padding
CGRect rendererRect = ASTextNodeAdjustRenderRectForShadowPadding(rectValue.rect, shadowPadding);
CGRect highlightedRect = [layer convertRect:rendererRect toLayer:highlightTargetLayer];
// We set our overlay layer's frame to the bounds of the highlight target layer.
// Offset highlight rects to avoid double-counting target layer's bounds.origin.
highlightedRect.origin.x -= highlightTargetLayer.bounds.origin.x;
highlightedRect.origin.y -= highlightTargetLayer.bounds.origin.y;
[converted addObject:[NSValue valueWithCGRect:highlightedRect]];
}
ASHighlightOverlayLayer *overlayLayer = [[ASHighlightOverlayLayer alloc] initWithRects:converted];
overlayLayer.highlightColor = [[self class] _highlightColorForStyle:self.highlightStyle];
overlayLayer.frame = highlightTargetLayer.bounds;
overlayLayer.masksToBounds = NO;
overlayLayer.opacity = [[self class] _highlightOpacityForStyle:self.highlightStyle];
[highlightTargetLayer addSublayer:overlayLayer];
if (animated) {
CABasicAnimation *fadeIn = [CABasicAnimation animationWithKeyPath:@"opacity"];
fadeIn.fromValue = @0.0;
fadeIn.toValue = @(overlayLayer.opacity);
fadeIn.duration = ASTextNodeHighlightFadeInDuration;
fadeIn.beginTime = CACurrentMediaTime();
[overlayLayer addAnimation:fadeIn forKey:fadeIn.keyPath];
}
[overlayLayer setNeedsDisplay];
_activeHighlightLayer = overlayLayer;
}
}
}
}
- (void)_clearHighlightIfNecessary
@@ -812,6 +911,12 @@ static NSArray *DefaultLinkAttributeNames() {
#pragma mark - Text rects
static CGRect ASTextNodeAdjustRenderRectForShadowPadding(CGRect rendererRect, UIEdgeInsets shadowPadding) {
rendererRect.origin.x -= shadowPadding.left;
rendererRect.origin.y -= shadowPadding.top;
return rendererRect;
}
- (NSArray *)rectsForTextRange:(NSRange)textRange
{
AS_TEXT_ALERT_UNIMPLEMENTED_FEATURE();
@@ -2146,7 +2146,9 @@ dispatch_semaphore_signal(_lock);
if (isVertical) {
topRect.rect = CGRectMake(startLine.left, topOffset, startLine.width, (_container.path ? startLine.bottom : _container.size.height - _container.insets.bottom) - topOffset);
} else {
topRect.rect = CGRectMake(topOffset, startLine.top, (_container.path ? startLine.right : _container.size.width - _container.insets.right) - topOffset, startLine.height);
// TODO: Fixes highlighting first row only to the end of the text and not highlight
// the while line to the end. Needs to brought over to multiline support
topRect.rect = CGRectMake(topOffset, startLine.top, (_container.path ? startLine.right : _container.size.width - _container.insets.right) - topOffset - (_container.size.width - _container.insets.right - startLine.right), startLine.height);
}
}
[rects addObject:topRect];
Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB