mirror of
https://github.com/TextureGroup/Texture.git
synced 2026-04-07 19:17:39 +00:00
Go further
This commit is contained in:
@@ -339,6 +339,8 @@
|
||||
CC57EAF71E3939350034C595 /* ASCollectionView+Undeprecated.h in Headers */ = {isa = PBXBuildFile; fileRef = CC2E317F1DAC353700EEE891 /* ASCollectionView+Undeprecated.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
CC57EAF81E3939450034C595 /* ASTableView+Undeprecated.h in Headers */ = {isa = PBXBuildFile; fileRef = CC512B841DAC45C60054848E /* ASTableView+Undeprecated.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
CC58AA4B1E398E1D002C8CB4 /* ASBlockTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = CC58AA4A1E398E1D002C8CB4 /* ASBlockTypes.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
CC5BE2891EC9174B0077A1A8 /* ASTextCaching.h in Headers */ = {isa = PBXBuildFile; fileRef = CC5BE2871EC9174B0077A1A8 /* ASTextCaching.h */; };
|
||||
CC5BE28A1EC9174B0077A1A8 /* ASTextCaching.m in Sources */ = {isa = PBXBuildFile; fileRef = CC5BE2881EC9174B0077A1A8 /* ASTextCaching.m */; };
|
||||
CC6AA2DA1E9F03B900978E87 /* ASDisplayNode+Ancestry.h in Headers */ = {isa = PBXBuildFile; fileRef = CC6AA2D81E9F03B900978E87 /* ASDisplayNode+Ancestry.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
CC6AA2DB1E9F03B900978E87 /* ASDisplayNode+Ancestry.m in Sources */ = {isa = PBXBuildFile; fileRef = CC6AA2D91E9F03B900978E87 /* ASDisplayNode+Ancestry.m */; };
|
||||
CC7FD9E11BB5F750005CCB2B /* ASPhotosFrameworkImageRequestTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CC7FD9E01BB5F750005CCB2B /* ASPhotosFrameworkImageRequestTests.m */; };
|
||||
@@ -790,6 +792,8 @@
|
||||
CC55A7101E52A0F200594372 /* ASResponderChainEnumerator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASResponderChainEnumerator.m; sourceTree = "<group>"; };
|
||||
CC57EAF91E394EA40034C595 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
CC58AA4A1E398E1D002C8CB4 /* ASBlockTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASBlockTypes.h; sourceTree = "<group>"; };
|
||||
CC5BE2871EC9174B0077A1A8 /* ASTextCaching.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASTextCaching.h; sourceTree = "<group>"; };
|
||||
CC5BE2881EC9174B0077A1A8 /* ASTextCaching.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASTextCaching.m; sourceTree = "<group>"; };
|
||||
CC6AA2D81E9F03B900978E87 /* ASDisplayNode+Ancestry.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "ASDisplayNode+Ancestry.h"; path = "Base/ASDisplayNode+Ancestry.h"; sourceTree = "<group>"; };
|
||||
CC6AA2D91E9F03B900978E87 /* ASDisplayNode+Ancestry.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "ASDisplayNode+Ancestry.m"; path = "Base/ASDisplayNode+Ancestry.m"; sourceTree = "<group>"; };
|
||||
CC7FD9DC1BB5E962005CCB2B /* ASPhotosFrameworkImageRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASPhotosFrameworkImageRequest.h; sourceTree = "<group>"; };
|
||||
@@ -1487,6 +1491,8 @@
|
||||
CCCCCCC11EC3EF060087FE10 /* TextExperiment */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
CC5BE2871EC9174B0077A1A8 /* ASTextCaching.h */,
|
||||
CC5BE2881EC9174B0077A1A8 /* ASTextCaching.m */,
|
||||
CCCCCCC21EC3EF060087FE10 /* Component */,
|
||||
CCCCCCCB1EC3EF060087FE10 /* String */,
|
||||
CCCCCCD01EC3EF060087FE10 /* Utility */,
|
||||
@@ -1642,6 +1648,7 @@
|
||||
B35062581B010F070018CF92 /* ASAvailability.h in Headers */,
|
||||
DE84918D1C8FFF2B003D89E9 /* ASRunLoopQueue.h in Headers */,
|
||||
CC0F88621E4281E200576FED /* ASSectionController.h in Headers */,
|
||||
CC5BE2891EC9174B0077A1A8 /* ASTextCaching.h in Headers */,
|
||||
A2763D7A1CBDD57D00A9ADBD /* ASPINRemoteImageDownloader.h in Headers */,
|
||||
34EFC7611B701C9C00AD841F /* ASBackgroundLayoutSpec.h in Headers */,
|
||||
B35062591B010F070018CF92 /* ASBaseDefines.h in Headers */,
|
||||
@@ -2099,6 +2106,7 @@
|
||||
DBDB83971C6E879900D0098C /* ASPagerFlowLayout.m in Sources */,
|
||||
E5B078001E69F4EB00C24B5B /* ASElementMap.m in Sources */,
|
||||
9C8898BC1C738BA800D6B02E /* ASTextKitFontSizeAdjuster.mm in Sources */,
|
||||
CC5BE28A1EC9174B0077A1A8 /* ASTextCaching.m in Sources */,
|
||||
690ED59B1E36D118000627C0 /* ASImageNode+tvOS.m in Sources */,
|
||||
CCCCCCD81EC3EF060087FE10 /* ASTextInput.m in Sources */,
|
||||
34EFC7621B701CA400AD841F /* ASBackgroundLayoutSpec.mm in Sources */,
|
||||
|
||||
@@ -177,7 +177,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
/**
|
||||
@abstract The style to use when highlighting text.
|
||||
*/
|
||||
@property (nonatomic, assign) ASTextNodeHighlightStyle highlightStyle;
|
||||
@property (atomic, assign) ASTextNodeHighlightStyle highlightStyle;
|
||||
|
||||
/**
|
||||
@abstract The range of text highlighted by the receiver. Changes to this property are not animated by default.
|
||||
|
||||
+60
-171
@@ -29,6 +29,7 @@
|
||||
|
||||
#import <AsyncDisplayKit/CoreGraphics+ASConvenience.h>
|
||||
#import <AsyncDisplayKit/ASObjectDescriptionHelpers.h>
|
||||
#import <AsyncDisplayKit/ASTextCaching.h>
|
||||
#import <AsyncDisplayKit/ASTextLayout.h>
|
||||
|
||||
// TODO: This is more like ASTextNode2State
|
||||
@@ -48,60 +49,36 @@
|
||||
|
||||
@implementation ASTextNode2DrawParameters
|
||||
|
||||
@end
|
||||
|
||||
@interface ASTextNode2CacheKey : NSObject
|
||||
- (instancetype)initForResultWithLayout:(ASTextLayout *)layout;
|
||||
- (instancetype)initForQueryWithParameters:(ASTextNode2DrawParameters *)parameters;
|
||||
@property (nonatomic, strong, readonly) NSAttributedString *attributedString;
|
||||
@property (nonatomic, strong, readonly) ASTextContainer *textContainer;
|
||||
@property (nonatomic, readonly) ASSizeRange resultSizeRange;
|
||||
@property (nonatomic, readonly) CGSize querySize;
|
||||
@end
|
||||
@implementation ASTextNode2CacheKey
|
||||
|
||||
- (instancetype)initForResultWithLayout:(ASTextLayout *)layout
|
||||
- (NSAttributedString *)processedAttributedText
|
||||
{
|
||||
if (self = [super init]) {
|
||||
_textContainer = layout.container;
|
||||
_attributedString = layout.text;
|
||||
_resultSizeRange = ASSizeRangeMakeLoosely(_textContainer.size, layout.textBoundingSize);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initForQueryWithParameters:(ASTextNode2DrawParameters *)parameters
|
||||
{
|
||||
if (self = [super init]) {
|
||||
_textContainer = parameters.textContainer;
|
||||
_attributedString = parameters.processedAttributedText;
|
||||
_querySize = _textContainer.size;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSUInteger)hash
|
||||
{
|
||||
// Just use attributed string for hash. Previously we included container attributes
|
||||
// in the hash but it actually deteriorated cache performance (more collisions).
|
||||
return _attributedString.hash;
|
||||
}
|
||||
|
||||
- (BOOL)isEqual:(id)object
|
||||
{
|
||||
ASDisplayNodeAssert([object isKindOfClass:[ASTextNode2CacheKey class]], @"Unexpected object: %@", object);
|
||||
ASTextNode2CacheKey *otherKey = object;
|
||||
if (![_attributedString isEqualToAttributedString:otherKey->_attributedString] || ![_textContainer isEqualToContainerExcludingSize:otherKey->_textContainer]) {
|
||||
return NO;
|
||||
}
|
||||
@synchronized (self) {
|
||||
if (_processedAttributedText) {
|
||||
return _processedAttributedText;
|
||||
}
|
||||
|
||||
ASDisplayNodeAssert(CGSizeEqualToSize(_querySize, CGSizeZero), @"Query-key should not be on the left-hand side of isEqual:.");
|
||||
|
||||
BOOL otherIsResult = CGSizeEqualToSize(otherKey->_querySize, CGSizeZero);
|
||||
if (otherIsResult) {
|
||||
return ASSizeRangeEqualToSizeRange(_resultSizeRange, otherKey->_resultSizeRange);
|
||||
} else {
|
||||
return ASSizeRangeContainsSize(_resultSizeRange, otherKey->_querySize);
|
||||
NSMutableAttributedString *attributedString = [_attributedText mutableCopy];
|
||||
// Apply paragraph style if needed
|
||||
[attributedString enumerateAttribute:NSParagraphStyleAttributeName inRange:NSMakeRange(0, attributedString.length) options:kNilOptions usingBlock:^(NSParagraphStyle *style, NSRange range, BOOL * _Nonnull stop) {
|
||||
if (style == nil || style.lineBreakMode == _lineBreakMode) {
|
||||
return;
|
||||
}
|
||||
|
||||
NSMutableParagraphStyle *paragraphStyle = [style mutableCopy] ?: [[NSMutableParagraphStyle alloc] init];
|
||||
paragraphStyle.lineBreakMode = _lineBreakMode;
|
||||
[attributedString addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:range];
|
||||
}];
|
||||
|
||||
// Apply background color if needed
|
||||
UIColor *backgroundColor = self.backgroundColor;
|
||||
if (CGColorGetAlpha(backgroundColor.CGColor) > 0) {
|
||||
[attributedString addAttribute:NSBackgroundColorAttributeName value:backgroundColor range:NSMakeRange(0, attributedString.length)];
|
||||
}
|
||||
|
||||
// Apply shadow if needed
|
||||
if (_shadow != nil) {
|
||||
[attributedString addAttribute:NSShadowAttributeName value:_shadow range:NSMakeRange(0, attributedString.length)];
|
||||
}
|
||||
_processedAttributedText = [attributedString copy];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,7 +105,7 @@ static const CGFloat ASTextNodeHighlightDarkOpacity = 0.22;
|
||||
static NSString *ASTextNodeTruncationTokenAttributeName = @"ASTextNodeTruncationAttribute";
|
||||
|
||||
@interface ASTextNode2 () <UIGestureRecognizerDelegate>
|
||||
@property (atomic, strong) ASTextNode2DrawParameters *displayedParameters;
|
||||
@property (atomic, strong) ASTextLayout *displayedTextLayout;
|
||||
@end
|
||||
|
||||
@implementation ASTextNode2 {
|
||||
@@ -145,6 +122,7 @@ static NSString *ASTextNodeTruncationTokenAttributeName = @"ASTextNodeTruncation
|
||||
NSAttributedString *_attributedText;
|
||||
NSAttributedString *_composedTruncationText;
|
||||
NSArray<NSNumber *> *_pointSizeScaleFactors;
|
||||
std::atomic<NSAttributedString *> _additionalTruncationMessage;
|
||||
|
||||
NSString *_highlightedLinkAttributeName;
|
||||
id _highlightedLinkAttributeValue;
|
||||
@@ -274,23 +252,31 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
|
||||
return YES;
|
||||
}
|
||||
|
||||
#define ASDeclareGetter(source, type, getterName) \
|
||||
- (type)getterName \
|
||||
{\
|
||||
return source;\
|
||||
}
|
||||
|
||||
#define ASDeclareSetter(expr, type, setterName, equal, sideEffect)\
|
||||
- (void)setterName(type)value \
|
||||
{ \
|
||||
BOOL sameValue = equal(expr, value); \
|
||||
if (sameValue) { \
|
||||
return; \
|
||||
}\
|
||||
expr = value; \
|
||||
sideEffect;\
|
||||
} \
|
||||
|
||||
#define ASDeclareReadwrite(expr, type, setterName, getterName, equal, sideEffect) \
|
||||
ASDeclareGetter(expr, type, getterName) \
|
||||
ASDeclareSetter(expr, type, setterName, equal, sideEffect)
|
||||
|
||||
ASDeclareReadwrite(_textContainer.insets, UIEdgeInsets, setTextContainerInset:, textContainerInset, UIEdgeInsetsEqualToEdgeInsets, [self setNeedsLayout]);
|
||||
|
||||
#pragma mark - Layout and Sizing
|
||||
|
||||
- (void)setTextContainerInset:(UIEdgeInsets)textContainerInset
|
||||
{
|
||||
BOOL needsUpdate = !UIEdgeInsetsEqualToEdgeInsets(_textContainer.insets, textContainerInset);
|
||||
_textContainer.insets = textContainerInset;
|
||||
|
||||
if (needsUpdate) {
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
}
|
||||
|
||||
- (UIEdgeInsets)textContainerInset
|
||||
{
|
||||
return _textContainer.insets;
|
||||
}
|
||||
|
||||
- (CGSize)calculateSizeThatFits:(CGSize)constrainedSize
|
||||
{
|
||||
ASDisplayNodeAssert(constrainedSize.width >= 0, @"Constrained width for text (%f) is too narrow", constrainedSize.width);
|
||||
@@ -298,7 +284,7 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
|
||||
|
||||
ASTextNode2DrawParameters *params = [self textNodeDrawParameters];
|
||||
params.textContainer.size = constrainedSize;
|
||||
ASTextLayout *layout = [ASTextNode2 compatibleLayoutWithParameters:params];
|
||||
ASTextLayout *layout = [ASTextLayoutCache layoutForText:params.processedAttributedText container:params.textContainer];
|
||||
|
||||
return layout.textBoundingSize;
|
||||
}
|
||||
@@ -382,41 +368,6 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
|
||||
return _textContainer.exclusionPaths;
|
||||
}
|
||||
|
||||
- (void)prepareAttributedStringForDrawing:(NSMutableAttributedString *)attributedString
|
||||
{
|
||||
ASDN::MutexLocker lock(__instanceLock__);
|
||||
|
||||
// Apply paragraph style if needed
|
||||
[attributedString enumerateAttribute:NSParagraphStyleAttributeName inRange:NSMakeRange(0, attributedString.length) options:kNilOptions usingBlock:^(NSParagraphStyle *style, NSRange range, BOOL * _Nonnull stop) {
|
||||
if (style == nil || style.lineBreakMode == _truncationMode) {
|
||||
return;
|
||||
}
|
||||
|
||||
NSMutableParagraphStyle *paragraphStyle = [style mutableCopy] ?: [[NSMutableParagraphStyle alloc] init];
|
||||
paragraphStyle.lineBreakMode = _truncationMode;
|
||||
[attributedString addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:range];
|
||||
}];
|
||||
|
||||
// Apply background color if needed
|
||||
UIColor *backgroundColor = self.backgroundColor;
|
||||
if (CGColorGetAlpha(backgroundColor.CGColor) > 0) {
|
||||
[attributedString addAttribute:NSBackgroundColorAttributeName value:backgroundColor range:NSMakeRange(0, attributedString.length)];
|
||||
}
|
||||
|
||||
// Apply shadow if needed
|
||||
if (_shadowOpacity > 0 && (_shadowRadius != 0 || !CGSizeEqualToSize(_shadowOffset, CGSizeZero)) && CGColorGetAlpha(_shadowColor) > 0) {
|
||||
NSShadow *shadow = [[NSShadow alloc] init];
|
||||
if (_shadowOpacity != 1) {
|
||||
shadow.shadowColor = [UIColor colorWithCGColor:CGColorCreateCopyWithAlpha(_shadowColor, _shadowOpacity * CGColorGetAlpha(_shadowColor))];
|
||||
} else {
|
||||
shadow.shadowColor = [UIColor colorWithCGColor:_shadowColor];
|
||||
}
|
||||
shadow.shadowOffset = _shadowOffset;
|
||||
shadow.shadowBlurRadius = _shadowRadius;
|
||||
[attributedString addAttribute:NSShadowAttributeName value:shadow range:NSMakeRange(0, attributedString.length)];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Drawing
|
||||
|
||||
// NOTE: This should be called something like `-drawParameters` but that's actually a (dumb) private ASDisplayNode method.
|
||||
@@ -447,7 +398,7 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
|
||||
{
|
||||
[super displayDidFinish];
|
||||
ASDisplayNodeAssertNotNil(_parametersForCurrentDisplay, @"Expected to have parameters for current display when display finishes.");
|
||||
_displayedParameters = _parametersForCurrentDisplay;
|
||||
_displayedTextLayout = [ASTextLayoutCache layoutForText:_parametersForCurrentDisplay.processedAttributedText container:_parametersForCurrentDisplay.textContainer];
|
||||
_parametersForCurrentDisplay = nil;
|
||||
}
|
||||
|
||||
@@ -468,50 +419,10 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
|
||||
return shadow;
|
||||
}
|
||||
|
||||
#define ENABLE_ASTEXT_CACHE_LOG 0
|
||||
/**
|
||||
* If it can't find a compatible layout, this method creates one.
|
||||
*/
|
||||
+ (ASTextLayout *)compatibleLayoutWithParameters:(ASTextNode2DrawParameters *)drawParameters
|
||||
{
|
||||
static NSCache<ASTextNode2CacheKey *, ASTextLayout *> *textLayoutCache2;
|
||||
#if ENABLE_ASTEXT_CACHE_LOG
|
||||
static std::atomic<int32_t> cacheHits;
|
||||
static std::atomic<int32_t> cacheQueries;
|
||||
#endif
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
textLayoutCache2 = [[NSCache alloc] init];
|
||||
});
|
||||
|
||||
#if ENABLE_ASTEXT_CACHE_LOG
|
||||
if (cacheQueries.load() % 5 == 0) {
|
||||
NSLog(@"%g", cacheHits.load() / (float)cacheQueries.load());
|
||||
}
|
||||
cacheQueries += 1;
|
||||
#endif
|
||||
auto cacheKey = [[ASTextNode2CacheKey alloc] initForQueryWithParameters:drawParameters];
|
||||
ASTextLayout *cachedLayout = [textLayoutCache2 objectForKey:cacheKey];
|
||||
if (cachedLayout != nil) {
|
||||
#if ENABLE_ASTEXT_CACHE_LOG
|
||||
cacheHits += 1;
|
||||
#endif
|
||||
return cachedLayout;
|
||||
}
|
||||
|
||||
// Cache Miss.
|
||||
|
||||
// Compute the text layout.
|
||||
ASTextLayout *layout = [ASTextLayout layoutWithContainer:drawParameters.textContainer text:drawParameters.processedAttributedText];
|
||||
ASTextNode2CacheKey *key = [[ASTextNode2CacheKey alloc] initForResultWithLayout:layout];
|
||||
[textLayoutCache2 setObject:layout forKey:key];
|
||||
return layout;
|
||||
}
|
||||
|
||||
+ (void)drawRect:(CGRect)bounds withParameters:(ASTextNode2DrawParameters *)drawParams isCancelled:(asdisplaynode_iscancelled_block_t)isCancelledBlock isRasterizing:(BOOL)isRasterizing;
|
||||
{
|
||||
drawParams.textContainer.size = bounds.size;
|
||||
ASTextLayout *layout = [self compatibleLayoutWithParameters:drawParams];
|
||||
ASTextLayout *layout = [ASTextLayoutCache layoutForText:drawParams.processedAttributedText container:drawParams.textContainer];
|
||||
|
||||
if (isCancelledBlock()) {
|
||||
return;
|
||||
@@ -541,7 +452,7 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
|
||||
inAdditionalTruncationMessage:(out BOOL *)inAdditionalTruncationMessageOut
|
||||
forHighlighting:(BOOL)highlighting
|
||||
{
|
||||
AS_TEXT_ALERT_UNIMPLEMENTED_FEATURE();
|
||||
auto range = [self.displayedTextLayout textRangeAtPoint:point];
|
||||
return nil;
|
||||
}
|
||||
|
||||
@@ -580,20 +491,6 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
|
||||
|
||||
#pragma mark - Highlighting
|
||||
|
||||
- (ASTextNodeHighlightStyle)highlightStyle
|
||||
{
|
||||
ASDN::MutexLocker l(__instanceLock__);
|
||||
|
||||
return _highlightStyle;
|
||||
}
|
||||
|
||||
- (void)setHighlightStyle:(ASTextNodeHighlightStyle)highlightStyle
|
||||
{
|
||||
ASDN::MutexLocker l(__instanceLock__);
|
||||
|
||||
_highlightStyle = highlightStyle;
|
||||
}
|
||||
|
||||
- (NSRange)highlightRange
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
@@ -928,14 +825,6 @@ static NSAttributedString *DefaultTruncationAttributedString()
|
||||
return defaultTruncationAttributedString;
|
||||
}
|
||||
|
||||
- (void)_ensureTruncationText
|
||||
{
|
||||
if (_textContainer.truncationToken == nil) {
|
||||
ASDN::MutexLocker l(__instanceLock__);
|
||||
_textContainer.truncationToken = [self _locked_composedTruncationText];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setTruncationAttributedText:(NSAttributedString *)truncationAttributedText
|
||||
{
|
||||
{
|
||||
@@ -956,7 +845,7 @@ static NSAttributedString *DefaultTruncationAttributedString()
|
||||
{
|
||||
ASDN::MutexLocker l(__instanceLock__);
|
||||
|
||||
if (ASObjectIsEqual(_additionalTruncationMessage, additionalTruncationMessage)) {
|
||||
if (ASObjectIsEqual(_additionalTruncationMessage.load(), additionalTruncationMessage)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -996,7 +885,7 @@ static NSAttributedString *DefaultTruncationAttributedString()
|
||||
|
||||
- (BOOL)isTruncated
|
||||
{
|
||||
return ([ASTextNode2 compatibleLayoutWithParameters:self.displayedParameters].truncatedLine != nil);
|
||||
return (_displayedTextLayout.truncatedLine != nil);
|
||||
}
|
||||
|
||||
- (NSUInteger)maximumNumberOfLines
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
//
|
||||
// ASTextCaching.h
|
||||
// Texture
|
||||
//
|
||||
// Copyright (c) 2017-present, Pinterest, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <AsyncDisplayKit/ASBaseDefines.h>
|
||||
|
||||
@class ASTextContainer, ASTextLayout;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
AS_SUBCLASSING_RESTRICTED
|
||||
@interface ASTextLayoutCache : NSObject
|
||||
|
||||
+ (ASTextLayout *)layoutForText:(NSAttributedString *)attributedText
|
||||
container:(ASTextContainer *)container;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,106 @@
|
||||
//
|
||||
// ASTextCaching.m
|
||||
// Texture
|
||||
//
|
||||
// Copyright (c) 2017-present, Pinterest, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
|
||||
#import "ASTextCaching.h"
|
||||
#import <AsyncDisplayKit/ASDimension.h>
|
||||
#import <AsyncDisplayKit/ASTextLayout.h>
|
||||
#import <AsyncDisplayKit/ASInternalHelpers.h>
|
||||
|
||||
@interface ASTextLayoutKey : NSObject
|
||||
- (instancetype)initForResultWithLayout:(ASTextLayout *)layout;
|
||||
|
||||
- (instancetype)initForQueryWithText:(NSAttributedString *)attributedString
|
||||
container:(ASTextContainer *)container;
|
||||
@property (nonatomic, strong, readonly) NSAttributedString *attributedString;
|
||||
@property (nonatomic, strong, readonly) ASTextContainer *textContainer;
|
||||
@property (nonatomic, readonly) ASSizeRange resultSizeRange;
|
||||
@property (nonatomic, readonly) CGSize querySize;
|
||||
@end
|
||||
|
||||
@implementation ASTextLayoutKey
|
||||
|
||||
- (instancetype)initForResultWithLayout:(ASTextLayout *)layout
|
||||
{
|
||||
if (self = [super init]) {
|
||||
_textContainer = layout.container;
|
||||
_attributedString = layout.text;
|
||||
_resultSizeRange = ASSizeRangeMakeLoosely(_textContainer.size, layout.textBoundingSize);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initForQueryWithText:(NSAttributedString *)attributedString container:(ASTextContainer *)container
|
||||
{
|
||||
if (self = [super init]) {
|
||||
_textContainer = container;
|
||||
_attributedString = attributedString;
|
||||
_querySize = container.size;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSUInteger)hash
|
||||
{
|
||||
// Just use attributed string for hash. Previously we included container attributes
|
||||
// in the hash but it actually deteriorated cache performance (more collisions).
|
||||
return _attributedString.hash;
|
||||
}
|
||||
|
||||
- (BOOL)isEqual:(id)object
|
||||
{
|
||||
ASDisplayNodeAssert([object isKindOfClass:[ASTextLayoutKey class]], @"Unexpected object: %@", object);
|
||||
ASTextLayoutKey *otherKey = object;
|
||||
if (![_attributedString isEqualToAttributedString:otherKey->_attributedString] || ![_textContainer isEqualToContainerExcludingSize:otherKey->_textContainer]) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
ASDisplayNodeAssert(CGSizeEqualToSize(_querySize, CGSizeZero), @"Query-key should not be on the left-hand side of isEqual:.");
|
||||
|
||||
BOOL otherIsResult = CGSizeEqualToSize(otherKey->_querySize, CGSizeZero);
|
||||
if (otherIsResult) {
|
||||
return ASSizeRangeEqualToSizeRange(_resultSizeRange, otherKey->_resultSizeRange);
|
||||
} else {
|
||||
return ASSizeRangeContainsSize(_resultSizeRange, otherKey->_querySize);
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation ASTextLayoutCache
|
||||
|
||||
#define ENABLE_ASTEXT_CACHE_LOG 0
|
||||
|
||||
+ (ASTextLayout *)layoutForText:(NSAttributedString *)attributedText
|
||||
container:(ASTextContainer *)container
|
||||
{
|
||||
static NSCache<ASTextLayoutKey *, ASTextLayout *> *textLayoutCache;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
textLayoutCache = [[NSCache alloc] init];
|
||||
});
|
||||
|
||||
ASTextLayoutKey *cacheKey = [[ASTextLayoutKey alloc] initForQueryWithText:attributedText container:container];
|
||||
ASTextLayout *cachedLayout = [textLayoutCache objectForKey:cacheKey];
|
||||
if (cachedLayout != nil) {
|
||||
return cachedLayout;
|
||||
}
|
||||
|
||||
// Cache Miss.
|
||||
|
||||
// Compute the text layout.
|
||||
ASTextLayout *layout = [ASTextLayout layoutWithContainer:container text:attributedText];
|
||||
ASTextLayoutKey *key = [[ASTextLayoutKey alloc] initForResultWithLayout:layout];
|
||||
[textLayoutCache setObject:layout forKey:key];
|
||||
return layout;
|
||||
}
|
||||
|
||||
@end
|
||||
Reference in New Issue
Block a user