mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-22 22:25:57 +00:00
Rename CK classes to AS classes
This commit is contained in:
committed by
Levi McCallum
parent
de66819286
commit
a0c05ebffc
@@ -10,17 +10,17 @@
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#ifndef ComponentKit_CKTextKitAttributes_h
|
||||
#define ComponentKit_CKTextKitAttributes_h
|
||||
#ifndef ComponentKit_ASTextKitAttributes_h
|
||||
#define ComponentKit_ASTextKitAttributes_h
|
||||
|
||||
@protocol CKTextKitTruncating;
|
||||
@protocol ASTextKitTruncating;
|
||||
|
||||
extern NSString *const CKTextKitTruncationAttributeName;
|
||||
extern NSString *const ASTextKitTruncationAttributeName;
|
||||
/**
|
||||
Use CKTextKitEntityAttribute as the value of this attribute to embed a link or other interactable content inside the
|
||||
Use ASTextKitEntityAttribute as the value of this attribute to embed a link or other interactable content inside the
|
||||
text.
|
||||
*/
|
||||
extern NSString *const CKTextKitEntityAttributeName;
|
||||
extern NSString *const ASTextKitEntityAttributeName;
|
||||
|
||||
static inline BOOL _objectsEqual(id<NSObject> obj1, id<NSObject> obj2)
|
||||
{
|
||||
@@ -30,19 +30,19 @@ static inline BOOL _objectsEqual(id<NSObject> obj1, id<NSObject> obj2)
|
||||
/**
|
||||
All NSObject values in this struct should be copied when passed into the TextComponent.
|
||||
*/
|
||||
struct CKTextKitAttributes {
|
||||
struct ASTextKitAttributes {
|
||||
/**
|
||||
The string to be drawn. CKTextKit will not augment this string with default colors, etc. so this must be complete.
|
||||
The string to be drawn. ASTextKit will not augment this string with default colors, etc. so this must be complete.
|
||||
*/
|
||||
NSAttributedString *attributedString;
|
||||
/**
|
||||
The string to use as the truncation string, usually just "...". If you have a range of text you would like to
|
||||
restrict highlighting to (for instance if you have "... Continue Reading", use the CKTextKitTruncationAttributeName
|
||||
restrict highlighting to (for instance if you have "... Continue Reading", use the ASTextKitTruncationAttributeName
|
||||
to mark the specific range of the string that should be highlightable.
|
||||
*/
|
||||
NSAttributedString *truncationAttributedString;
|
||||
/**
|
||||
This is the character set that CKTextKit should attempt to avoid leaving as a trailing character before your
|
||||
This is the character set that ASTextKit should attempt to avoid leaving as a trailing character before your
|
||||
truncation token. By default this set includes "\s\t\n\r.,!?:;" so you don't end up with ugly looking truncation
|
||||
text like "Hey, this is some fancy Truncation!\n\n...". Instead it would be truncated as "Hey, this is some fancy
|
||||
truncation...". This is not always possible.
|
||||
@@ -90,7 +90,7 @@ struct CKTextKitAttributes {
|
||||
We provide an explicit copy function so we can use aggregate initializer syntax while providing copy semantics for
|
||||
the NSObjects inside.
|
||||
*/
|
||||
const CKTextKitAttributes copy() const
|
||||
const ASTextKitAttributes copy() const
|
||||
{
|
||||
return {
|
||||
[attributedString copy],
|
||||
@@ -107,7 +107,7 @@ struct CKTextKitAttributes {
|
||||
};
|
||||
};
|
||||
|
||||
bool operator==(const CKTextKitAttributes &other) const
|
||||
bool operator==(const ASTextKitAttributes &other) const
|
||||
{
|
||||
// These comparisons are in a specific order to reduce the overall cost of this function.
|
||||
return lineBreakMode == other.lineBreakMode
|
||||
@@ -8,16 +8,16 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#import "CKTextKitAttributes.h"
|
||||
#import "ASTextKitAttributes.h"
|
||||
|
||||
#import "CKEqualityHashHelpers.h"
|
||||
#import "ASEqualityHashHelpers.h"
|
||||
|
||||
#include <functional>
|
||||
|
||||
NSString *const CKTextKitTruncationAttributeName = @"ck_truncation";
|
||||
NSString *const CKTextKitEntityAttributeName = @"ck_entity";
|
||||
NSString *const ASTextKitTruncationAttributeName = @"ck_truncation";
|
||||
NSString *const ASTextKitEntityAttributeName = @"ck_entity";
|
||||
|
||||
size_t CKTextKitAttributes::hash() const
|
||||
size_t ASTextKitAttributes::hash() const
|
||||
{
|
||||
NSUInteger subhashes[] = {
|
||||
[attributedString hash],
|
||||
@@ -11,12 +11,12 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
/**
|
||||
A threadsafe container for the TextKit components that CKTextKit uses to lay out and truncate its text.
|
||||
A threadsafe container for the TextKit components that ASTextKit uses to lay out and truncate its text.
|
||||
|
||||
This container is the sole owner and manager of the TextKit classes. This is an important model because of major
|
||||
thread safety issues inside vanilla TextKit. It provides a central locking location for accessing TextKit methods.
|
||||
*/
|
||||
@interface CKTextKitContext : NSObject
|
||||
@interface ASTextKitContext : NSObject
|
||||
|
||||
/**
|
||||
Initializes a context and its associated TextKit components.
|
||||
@@ -10,9 +10,9 @@
|
||||
|
||||
#import <mutex>
|
||||
|
||||
#import "CKTextKitContext.h"
|
||||
#import "ASTextKitContext.h"
|
||||
|
||||
@implementation CKTextKitContext
|
||||
@implementation ASTextKitContext
|
||||
{
|
||||
// All TextKit operations (even non-mutative ones) must be executed serially.
|
||||
std::mutex _textKitMutex;
|
||||
83
AsyncDisplayKit/TextKit/ASTextKitCoreTextAdditions.h
Normal file
83
AsyncDisplayKit/TextKit/ASTextKitCoreTextAdditions.h
Normal file
@@ -0,0 +1,83 @@
|
||||
/* Copyright (c) 2014-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 <Foundation/Foundation.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import <AsyncDisplayKit/ASBaseDefines.h>
|
||||
|
||||
|
||||
ASDISPLAYNODE_EXTERN_C_BEGIN
|
||||
/**
|
||||
@abstract Returns whether a given attribute is an unsupported Core Text attribute.
|
||||
@param attributeName The name of the attribute
|
||||
@discussion The following Core Text attributes are not supported on NSAttributedString, and thus will not be preserved during the conversion:
|
||||
- kCTForegroundColorFromContextAttributeName
|
||||
- kCTSuperscriptAttributeName
|
||||
- kCTGlyphInfoAttributeName
|
||||
- kCTCharacterShapeAttributeName
|
||||
- kCTLanguageAttributeName
|
||||
- kCTRunDelegateAttributeName
|
||||
- kCTBaselineClassAttributeName
|
||||
- kCTBaselineInfoAttributeName
|
||||
- kCTBaselineReferenceInfoAttributeName
|
||||
- kCTWritingDirectionAttributeName
|
||||
- kCTUnderlineColorAttributeName
|
||||
@result Whether attributeName is an unsupported Core Text attribute.
|
||||
*/
|
||||
BOOL ASAttributeWithNameIsUnsupportedCoreTextAttribute(NSString *attributeName);
|
||||
|
||||
|
||||
/**
|
||||
@abstract Returns an attributes dictionary for use by NSAttributedString, given a dictionary of Core Text attributes.
|
||||
@param coreTextAttributes An NSDictionary whose keys are CFAttributedStringRef attributes.
|
||||
@discussion The following Core Text attributes are not supported on NSAttributedString, and thus will not be preserved during the conversion:
|
||||
- kCTForegroundColorFromContextAttributeName
|
||||
- kCTSuperscriptAttributeName
|
||||
- kCTGlyphInfoAttributeName
|
||||
- kCTCharacterShapeAttributeName
|
||||
- kCTLanguageAttributeName
|
||||
- kCTRunDelegateAttributeName
|
||||
- kCTBaselineClassAttributeName
|
||||
- kCTBaselineInfoAttributeName
|
||||
- kCTBaselineReferenceInfoAttributeName
|
||||
- kCTWritingDirectionAttributeName
|
||||
- kCTUnderlineColorAttributeName
|
||||
@result An NSDictionary of attributes for use by NSAttributedString.
|
||||
*/
|
||||
extern NSDictionary *NSAttributedStringAttributesForCoreTextAttributes(NSDictionary *coreTextAttributes);
|
||||
|
||||
/**
|
||||
@abstract Returns an NSAttributedString whose Core Text attributes have been converted, where possible, to NSAttributedString attributes.
|
||||
@param dirtyAttributedString An NSAttributedString that may contain Core Text attributes.
|
||||
@result An NSAttributedString that's preserved as many CFAttributedString attributes as possible.
|
||||
*/
|
||||
extern NSAttributedString *ASCleanseAttributedStringOfCoreTextAttributes(NSAttributedString *dirtyAttributedString);
|
||||
|
||||
ASDISPLAYNODE_EXTERN_C_END
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark -
|
||||
@interface NSParagraphStyle (ASTextKitCoreTextAdditions)
|
||||
|
||||
/**
|
||||
@abstract Returns an NSParagraphStyle initialized with the paragraph specifiers from the given CTParagraphStyleRef.
|
||||
@param coreTextParagraphStyle A Core Text paragraph style.
|
||||
@discussion It is important to note that not all CTParagraphStyle specifiers are supported by NSParagraphStyle, and consequently, this is a lossy conversion. Notably, the following specifiers will not preserved:
|
||||
- kCTParagraphStyleSpecifierTabStops
|
||||
- kCTParagraphStyleSpecifierDefaultTabInterval
|
||||
- kCTParagraphStyleSpecifierMaximumLineSpacing
|
||||
- kCTParagraphStyleSpecifierMinimumLineSpacing
|
||||
- kCTParagraphStyleSpecifierLineSpacingAdjustment
|
||||
- kCTParagraphStyleSpecifierLineBoundsOptions
|
||||
@result An NSParagraphStyle initializd with as many of the paragraph specifiers from `coreTextParagraphStyle` as possible.
|
||||
|
||||
*/
|
||||
+ (instancetype)paragraphStyleWithCTParagraphStyle:(CTParagraphStyleRef)coreTextParagraphStyle;
|
||||
|
||||
@end
|
||||
246
AsyncDisplayKit/TextKit/ASTextKitCoreTextAdditions.m
Normal file
246
AsyncDisplayKit/TextKit/ASTextKitCoreTextAdditions.m
Normal file
@@ -0,0 +1,246 @@
|
||||
/* Copyright (c) 2014-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 "ASTextKitCoreTextAdditions.h"
|
||||
|
||||
#import <CoreText/CTFont.h>
|
||||
#import <CoreText/CTStringAttributes.h>
|
||||
|
||||
#import "ASAssert.h"
|
||||
|
||||
#pragma mark - Public
|
||||
BOOL ASAttributeWithNameIsUnsupportedCoreTextAttribute(NSString *attributeName)
|
||||
{
|
||||
static NSSet *coreTextAttributes;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
coreTextAttributes = [NSSet setWithObjects:(__bridge id)kCTForegroundColorAttributeName,
|
||||
kCTForegroundColorFromContextAttributeName,
|
||||
kCTForegroundColorAttributeName,
|
||||
kCTStrokeColorAttributeName,
|
||||
kCTUnderlineStyleAttributeName,
|
||||
kCTVerticalFormsAttributeName,
|
||||
kCTRunDelegateAttributeName,
|
||||
kCTBaselineClassAttributeName,
|
||||
kCTBaselineInfoAttributeName,
|
||||
kCTBaselineReferenceInfoAttributeName,
|
||||
kCTUnderlineColorAttributeName,
|
||||
nil];
|
||||
});
|
||||
return [coreTextAttributes containsObject:attributeName];
|
||||
}
|
||||
|
||||
NSDictionary *NSAttributedStringAttributesForCoreTextAttributes(NSDictionary *coreTextAttributes)
|
||||
{
|
||||
NSMutableDictionary *cleanAttributes = [[NSMutableDictionary alloc] initWithCapacity:coreTextAttributes.count];
|
||||
|
||||
[coreTextAttributes enumerateKeysAndObjectsUsingBlock:^(NSString *coreTextKey, id coreTextValue, BOOL *stop) {
|
||||
// The following attributes are not supported on NSAttributedString. Should they become available, we should add them.
|
||||
/*
|
||||
kCTForegroundColorFromContextAttributeName
|
||||
kCTSuperscriptAttributeName
|
||||
kCTGlyphInfoAttributeName
|
||||
kCTCharacterShapeAttributeName
|
||||
kCTLanguageAttributeName
|
||||
kCTRunDelegateAttributeName
|
||||
kCTBaselineClassAttributeName
|
||||
kCTBaselineInfoAttributeName
|
||||
kCTBaselineReferenceInfoAttributeName
|
||||
kCTWritingDirectionAttributeName
|
||||
kCTUnderlineColorAttributeName
|
||||
*/
|
||||
|
||||
// Conversely, the following attributes are not supported on CFAttributedString. Should they become available, we should add them.
|
||||
/*
|
||||
NSStrikethroughStyleAttributeName
|
||||
NSShadowAttributeName
|
||||
NSBackgroundColorAttributeName
|
||||
*/
|
||||
|
||||
// kCTFontAttributeName -> NSFontAttributeName
|
||||
if ([coreTextKey isEqualToString:(NSString *)kCTFontAttributeName]) {
|
||||
CTFontRef coreTextFont = (__bridge CTFontRef)coreTextValue;
|
||||
NSString *fontName = (__bridge_transfer NSString *)CTFontCopyPostScriptName(coreTextFont);
|
||||
CGFloat fontSize = CTFontGetSize(coreTextFont);
|
||||
UIFont *font = [UIFont fontWithName:fontName size:fontSize];
|
||||
ASDisplayNodeCAssertNotNil(font, @"unable to load font %@ with size %f", fontName, fontSize);
|
||||
if (font == nil) {
|
||||
// Gracefully fail if we were unable to load the font.
|
||||
font = [UIFont systemFontOfSize:fontSize];
|
||||
}
|
||||
cleanAttributes[NSFontAttributeName] = font;
|
||||
}
|
||||
// kCTKernAttributeName -> NSKernAttributeName
|
||||
else if ([coreTextKey isEqualToString:(NSString *)kCTKernAttributeName]) {
|
||||
cleanAttributes[NSKernAttributeName] = (NSNumber *)coreTextValue;
|
||||
}
|
||||
// kCTLigatureAttributeName -> NSLigatureAttributeName
|
||||
else if ([coreTextKey isEqualToString:(NSString *)kCTLigatureAttributeName]) {
|
||||
cleanAttributes[NSLigatureAttributeName] = (NSNumber *)coreTextValue;
|
||||
}
|
||||
// kCTForegroundColorAttributeName -> NSForegroundColorAttributeName
|
||||
else if ([coreTextKey isEqualToString:(NSString *)kCTForegroundColorAttributeName]) {
|
||||
cleanAttributes[NSForegroundColorAttributeName] = [UIColor colorWithCGColor:(CGColorRef)coreTextValue];
|
||||
}
|
||||
// kCTParagraphStyleAttributeName -> NSParagraphStyleAttributeName
|
||||
else if ([coreTextKey isEqualToString:(NSString *)kCTParagraphStyleAttributeName]) {
|
||||
cleanAttributes[NSParagraphStyleAttributeName] = [NSParagraphStyle paragraphStyleWithCTParagraphStyle:(CTParagraphStyleRef)coreTextValue];
|
||||
}
|
||||
// kCTStrokeWidthAttributeName -> NSStrokeWidthAttributeName
|
||||
else if ([coreTextKey isEqualToString:(NSString *)kCTStrokeWidthAttributeName]) {
|
||||
cleanAttributes[NSStrokeWidthAttributeName] = (NSNumber *)coreTextValue;
|
||||
}
|
||||
// kCTStrokeColorAttributeName -> NSStrokeColorAttributeName
|
||||
else if ([coreTextKey isEqualToString:(NSString *)kCTStrokeColorAttributeName]) {
|
||||
cleanAttributes[NSStrokeColorAttributeName] = [UIColor colorWithCGColor:(CGColorRef)coreTextValue];
|
||||
}
|
||||
// kCTUnderlineStyleAttributeName -> NSUnderlineStyleAttributeName
|
||||
else if ([coreTextKey isEqualToString:(NSString *)kCTUnderlineStyleAttributeName]) {
|
||||
cleanAttributes[NSUnderlineStyleAttributeName] = (NSNumber *)coreTextValue;
|
||||
}
|
||||
// kCTVerticalFormsAttributeName -> NSVerticalGlyphFormAttributeName
|
||||
else if ([coreTextKey isEqualToString:(NSString *)kCTVerticalFormsAttributeName]) {
|
||||
BOOL flag = (BOOL)CFBooleanGetValue((CFBooleanRef)coreTextValue);
|
||||
cleanAttributes[NSVerticalGlyphFormAttributeName] = @((int)flag); // NSVerticalGlyphFormAttributeName is documented to be an NSNumber with an integer that's either 0 or 1.
|
||||
}
|
||||
// Don't filter out any internal text attributes
|
||||
else if (!ASAttributeWithNameIsUnsupportedCoreTextAttribute(coreTextKey)){
|
||||
cleanAttributes[coreTextKey] = coreTextValue;
|
||||
}
|
||||
}];
|
||||
|
||||
return cleanAttributes;
|
||||
}
|
||||
|
||||
NSAttributedString *ASCleanseAttributedStringOfCoreTextAttributes(NSAttributedString *dirtyAttributedString)
|
||||
{
|
||||
if (!dirtyAttributedString)
|
||||
return nil;
|
||||
|
||||
// First see if there are any core text attributes on the string
|
||||
__block BOOL containsCoreTextAttributes = NO;
|
||||
[dirtyAttributedString enumerateAttributesInRange:NSMakeRange(0, dirtyAttributedString.length)
|
||||
options:0
|
||||
usingBlock:^(NSDictionary *dirtyAttributes, NSRange range, BOOL *stop) {
|
||||
[dirtyAttributes enumerateKeysAndObjectsUsingBlock:^(NSString *coreTextKey, id coreTextValue, BOOL *innerStop) {
|
||||
if (ASAttributeWithNameIsUnsupportedCoreTextAttribute(coreTextKey)) {
|
||||
containsCoreTextAttributes = YES;
|
||||
*innerStop = YES;
|
||||
}
|
||||
}];
|
||||
*stop = containsCoreTextAttributes;
|
||||
}];
|
||||
if (containsCoreTextAttributes) {
|
||||
|
||||
NSString *plainString = dirtyAttributedString.string;
|
||||
NSMutableAttributedString *cleanAttributedString = [[NSMutableAttributedString alloc] initWithString:plainString];
|
||||
|
||||
// Iterate over all of the attributes, cleaning them as appropriate and applying them as we go.
|
||||
[dirtyAttributedString enumerateAttributesInRange:NSMakeRange(0, plainString.length)
|
||||
options:0
|
||||
usingBlock:^(NSDictionary *dirtyAttributes, NSRange range, BOOL *stop) {
|
||||
[cleanAttributedString addAttributes:NSAttributedStringAttributesForCoreTextAttributes(dirtyAttributes) range:range];
|
||||
}];
|
||||
|
||||
return cleanAttributedString;
|
||||
} else {
|
||||
return [dirtyAttributedString copy];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark -
|
||||
@implementation NSParagraphStyle (ASTextKitCoreTextAdditions)
|
||||
|
||||
+ (instancetype)paragraphStyleWithCTParagraphStyle:(CTParagraphStyleRef)coreTextParagraphStyle;
|
||||
{
|
||||
NSMutableParagraphStyle *newParagraphStyle = [[NSMutableParagraphStyle alloc] init];
|
||||
|
||||
if (!coreTextParagraphStyle)
|
||||
return newParagraphStyle;
|
||||
|
||||
// The following paragraph style specifiers are not supported on NSParagraphStyle. Should they become available, we should add them.
|
||||
/*
|
||||
kCTParagraphStyleSpecifierTabStops
|
||||
kCTParagraphStyleSpecifierDefaultTabInterval
|
||||
kCTParagraphStyleSpecifierMaximumLineSpacing
|
||||
kCTParagraphStyleSpecifierMinimumLineSpacing
|
||||
kCTParagraphStyleSpecifierLineSpacingAdjustment
|
||||
kCTParagraphStyleSpecifierLineBoundsOptions
|
||||
*/
|
||||
|
||||
// Conversely, the following paragraph styles are not supported on CTParagraphStyle. Should they become available, we should add them.
|
||||
/*
|
||||
hyphenationFactor
|
||||
*/
|
||||
|
||||
// kCTParagraphStyleSpecifierAlignment -> alignment
|
||||
CTTextAlignment coreTextAlignment;
|
||||
if (CTParagraphStyleGetValueForSpecifier(coreTextParagraphStyle, kCTParagraphStyleSpecifierAlignment, sizeof(coreTextAlignment), &coreTextAlignment))
|
||||
newParagraphStyle.alignment = NSTextAlignmentFromCTTextAlignment(coreTextAlignment);
|
||||
|
||||
// kCTParagraphStyleSpecifierFirstLineHeadIndent -> firstLineHeadIndent
|
||||
CGFloat firstLineHeadIndent;
|
||||
if (CTParagraphStyleGetValueForSpecifier(coreTextParagraphStyle, kCTParagraphStyleSpecifierFirstLineHeadIndent, sizeof(firstLineHeadIndent), &firstLineHeadIndent))
|
||||
newParagraphStyle.firstLineHeadIndent = firstLineHeadIndent;
|
||||
|
||||
// kCTParagraphStyleSpecifierHeadIndent -> headIndent
|
||||
CGFloat headIndent;
|
||||
if (CTParagraphStyleGetValueForSpecifier(coreTextParagraphStyle, kCTParagraphStyleSpecifierHeadIndent, sizeof(headIndent), &headIndent))
|
||||
newParagraphStyle.headIndent = headIndent;
|
||||
|
||||
// kCTParagraphStyleSpecifierTailIndent -> tailIndent
|
||||
CGFloat tailIndent;
|
||||
if (CTParagraphStyleGetValueForSpecifier(coreTextParagraphStyle, kCTParagraphStyleSpecifierTailIndent, sizeof(tailIndent), &tailIndent))
|
||||
newParagraphStyle.tailIndent = tailIndent;
|
||||
|
||||
// kCTParagraphStyleSpecifierLineBreakMode -> lineBreakMode
|
||||
CTLineBreakMode coreTextLineBreakMode;
|
||||
if (CTParagraphStyleGetValueForSpecifier(coreTextParagraphStyle, kCTParagraphStyleSpecifierLineBreakMode, sizeof(coreTextLineBreakMode), &coreTextLineBreakMode))
|
||||
newParagraphStyle.lineBreakMode = (NSLineBreakMode)coreTextLineBreakMode; // They're the same enum.
|
||||
|
||||
// kCTParagraphStyleSpecifierLineHeightMultiple -> lineHeightMultiple
|
||||
CGFloat lineHeightMultiple;
|
||||
if (CTParagraphStyleGetValueForSpecifier(coreTextParagraphStyle, kCTParagraphStyleSpecifierLineHeightMultiple, sizeof(lineHeightMultiple), &lineHeightMultiple))
|
||||
newParagraphStyle.lineHeightMultiple = lineHeightMultiple;
|
||||
|
||||
// kCTParagraphStyleSpecifierMaximumLineHeight -> maximumLineHeight
|
||||
CGFloat maximumLineHeight;
|
||||
if (CTParagraphStyleGetValueForSpecifier(coreTextParagraphStyle, kCTParagraphStyleSpecifierMaximumLineHeight, sizeof(maximumLineHeight), &maximumLineHeight))
|
||||
newParagraphStyle.maximumLineHeight = maximumLineHeight;
|
||||
|
||||
// kCTParagraphStyleSpecifierMinimumLineHeight -> minimumLineHeight
|
||||
CGFloat minimumLineHeight;
|
||||
if (CTParagraphStyleGetValueForSpecifier(coreTextParagraphStyle, kCTParagraphStyleSpecifierMinimumLineHeight, sizeof(minimumLineHeight), &minimumLineHeight))
|
||||
newParagraphStyle.minimumLineHeight = minimumLineHeight;
|
||||
|
||||
// kCTParagraphStyleSpecifierLineSpacing -> lineSpacing
|
||||
// Note that kCTParagraphStyleSpecifierLineSpacing is deprecated and will die soon. We should not be using it.
|
||||
CGFloat lineSpacing;
|
||||
if (CTParagraphStyleGetValueForSpecifier(coreTextParagraphStyle, kCTParagraphStyleSpecifierLineSpacing, sizeof(lineSpacing), &lineSpacing))
|
||||
newParagraphStyle.lineSpacing = lineSpacing;
|
||||
|
||||
// kCTParagraphStyleSpecifierParagraphSpacing -> paragraphSpacing
|
||||
CGFloat paragraphSpacing;
|
||||
if (CTParagraphStyleGetValueForSpecifier(coreTextParagraphStyle, kCTParagraphStyleSpecifierParagraphSpacing, sizeof(paragraphSpacing), ¶graphSpacing))
|
||||
newParagraphStyle.paragraphSpacing = paragraphSpacing;
|
||||
|
||||
// kCTParagraphStyleSpecifierParagraphSpacingBefore -> paragraphSpacingBefore
|
||||
CGFloat paragraphSpacingBefore;
|
||||
if (CTParagraphStyleGetValueForSpecifier(coreTextParagraphStyle, kCTParagraphStyleSpecifierParagraphSpacingBefore, sizeof(paragraphSpacingBefore), ¶graphSpacingBefore))
|
||||
newParagraphStyle.paragraphSpacingBefore = paragraphSpacingBefore;
|
||||
|
||||
// kCTParagraphStyleSpecifierBaseWritingDirection -> baseWritingDirection
|
||||
CTWritingDirection coreTextBaseWritingDirection;
|
||||
if (CTParagraphStyleGetValueForSpecifier(coreTextParagraphStyle, kCTParagraphStyleSpecifierBaseWritingDirection, sizeof(coreTextBaseWritingDirection), &coreTextBaseWritingDirection))
|
||||
newParagraphStyle.baseWritingDirection = (NSWritingDirection)coreTextBaseWritingDirection; // They're the same enum.
|
||||
|
||||
return newParagraphStyle;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -11,7 +11,7 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
/**
|
||||
The object that should be embedded with CKTextKitEntityAttributeName. Please note that the entity you provide MUST
|
||||
The object that should be embedded with ASTextKitEntityAttributeName. Please note that the entity you provide MUST
|
||||
implement a proper hash and isEqual function or your application performance will grind to a halt due to
|
||||
NSMutableAttributedString's usage of a global hash table of all attributes. This means the entity should NOT be a
|
||||
Foundation Collection (NSArray, NSDictionary, NSSet, etc.) since their hash function is a simple count of the values
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
rdar://19352367
|
||||
*/
|
||||
@interface CKTextKitEntityAttribute : NSObject
|
||||
@interface ASTextKitEntityAttribute : NSObject
|
||||
|
||||
@property (nonatomic, strong, readonly) id<NSObject> entity;
|
||||
|
||||
@@ -8,9 +8,9 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#import "CKTextKitEntityAttribute.h"
|
||||
#import "ASTextKitEntityAttribute.h"
|
||||
|
||||
@implementation CKTextKitEntityAttribute
|
||||
@implementation ASTextKitEntityAttribute
|
||||
|
||||
- (instancetype)initWithEntity:(id<NSObject>)entity
|
||||
{
|
||||
@@ -33,7 +33,7 @@
|
||||
if (![object isKindOfClass:[self class]]) {
|
||||
return NO;
|
||||
}
|
||||
CKTextKitEntityAttribute *other = (CKTextKitEntityAttribute *)object;
|
||||
ASTextKitEntityAttribute *other = (ASTextKitEntityAttribute *)object;
|
||||
return _entity == other.entity || [_entity isEqual:other.entity];
|
||||
}
|
||||
|
||||
54
AsyncDisplayKit/TextKit/ASTextKitHelpers.h
Normal file
54
AsyncDisplayKit/TextKit/ASTextKitHelpers.h
Normal file
@@ -0,0 +1,54 @@
|
||||
/* Copyright (c) 2014-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 <objc/message.h>
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "ASBaseDefines.h"
|
||||
|
||||
ASDISPLAYNODE_INLINE CGFloat ceilPixelValueForScale(CGFloat f, CGFloat scale)
|
||||
{
|
||||
// Round up to device pixel (.5 on retina)
|
||||
return ceilf(f * scale) / scale;
|
||||
}
|
||||
|
||||
ASDISPLAYNODE_INLINE CGSize ceilSizeValue(CGSize s)
|
||||
{
|
||||
CGFloat screenScale = [UIScreen mainScreen].scale;
|
||||
s.width = ceilPixelValueForScale(s.width, screenScale);
|
||||
s.height = ceilPixelValueForScale(s.height, screenScale);
|
||||
return s;
|
||||
}
|
||||
|
||||
@interface ASTextKitComponents : NSObject
|
||||
|
||||
/**
|
||||
@abstract Creates the stack of TextKit components.
|
||||
@param attributedSeedString The attributed string to seed the returned text storage with, or nil to receive an blank text storage.
|
||||
@param textContainerSize The size of the text-container. Typically, size specifies the constraining width of the layout, and FLT_MAX for height. Pass CGSizeZero if these components will be hooked up to a UITextView, which will manage the text container's size itself.
|
||||
@return An `ASTextKitComponents` containing the created components. The text view component will be nil.
|
||||
@discussion The returned components will be hooked up together, so they are ready for use as a system upon return.
|
||||
*/
|
||||
+ (ASTextKitComponents *)componentsWithAttributedSeedString:(NSAttributedString *)attributedSeedString
|
||||
textContainerSize:(CGSize)textContainerSize;
|
||||
|
||||
/**
|
||||
@abstract Returns the bounding size for the text view's text.
|
||||
@param components The TextKit components to calculate the constrained size of the text for.
|
||||
@param constrainedWidth The constraining width to be used during text-sizing. Usually, this value should be the receiver's calculated size.
|
||||
@result A CGSize representing the bounding size for the receiver's text.
|
||||
*/
|
||||
- (CGSize)sizeForConstrainedWidth:(CGFloat)constrainedWidth;
|
||||
|
||||
@property (nonatomic, strong, readonly) NSTextStorage *textStorage;
|
||||
@property (nonatomic, strong, readonly) NSTextContainer *textContainer;
|
||||
@property (nonatomic, strong, readonly) NSLayoutManager *layoutManager;
|
||||
@property (nonatomic, strong) UITextView *textView;
|
||||
|
||||
@end
|
||||
57
AsyncDisplayKit/TextKit/ASTextKitHelpers.mm
Normal file
57
AsyncDisplayKit/TextKit/ASTextKitHelpers.mm
Normal file
@@ -0,0 +1,57 @@
|
||||
/* Copyright (c) 2014-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 "ASTextKitHelpers.h"
|
||||
|
||||
@interface ASTextKitComponents ()
|
||||
|
||||
// read-write redeclarations
|
||||
@property (nonatomic, strong, readwrite) NSTextStorage *textStorage;
|
||||
@property (nonatomic, strong, readwrite) NSTextContainer *textContainer;
|
||||
@property (nonatomic, strong, readwrite) NSLayoutManager *layoutManager;
|
||||
|
||||
@end
|
||||
|
||||
@implementation ASTextKitComponents
|
||||
|
||||
+ (ASTextKitComponents *)componentsWithAttributedSeedString:(NSAttributedString *)attributedSeedString
|
||||
textContainerSize:(CGSize)textContainerSize
|
||||
{
|
||||
ASTextKitComponents *components = [[ASTextKitComponents alloc] init];
|
||||
|
||||
// Create the TextKit component stack with our default configuration.
|
||||
components.textStorage = (attributedSeedString ? [[NSTextStorage alloc] initWithAttributedString:attributedSeedString] : [[NSTextStorage alloc] init]);
|
||||
|
||||
components.layoutManager = [[NSLayoutManager alloc] init];
|
||||
[components.textStorage addLayoutManager:components.layoutManager];
|
||||
|
||||
components.textContainer = [[NSTextContainer alloc] initWithSize:textContainerSize];
|
||||
components.textContainer.lineFragmentPadding = 0.0; // We want the text laid out up to the very edges of the text-view.
|
||||
[components.layoutManager addTextContainer:components.textContainer];
|
||||
|
||||
return components;
|
||||
}
|
||||
|
||||
- (CGSize)sizeForConstrainedWidth:(CGFloat)constrainedWidth
|
||||
{
|
||||
ASTextKitComponents *components = self;
|
||||
|
||||
// If our text-view's width is already the constrained width, we can use our existing TextKit stack for this sizing calculation.
|
||||
// Otherwise, we create a temporary stack to size for `constrainedWidth`.
|
||||
if (CGRectGetWidth(components.textView.bounds) != constrainedWidth) {
|
||||
components = [ASTextKitComponents componentsWithAttributedSeedString:components.textStorage textContainerSize:CGSizeMake(constrainedWidth, FLT_MAX)];
|
||||
}
|
||||
|
||||
// Force glyph generation and layout, which may not have happened yet (and isn't triggered by -usedRectForTextContainer:).
|
||||
[components.layoutManager ensureLayoutForTextContainer:components.textContainer];
|
||||
CGSize textSize = [components.layoutManager usedRectForTextContainer:components.textContainer].size;
|
||||
|
||||
return textSize;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#import "CKTextKitRenderer.h"
|
||||
#import "ASTextKitRenderer.h"
|
||||
|
||||
typedef void (^ck_text_component_index_block_t)(NSUInteger characterIndex,
|
||||
CGRect glyphBoundingRect,
|
||||
@@ -25,13 +25,13 @@ typedef void (^ck_text_component_index_block_t)(NSUInteger characterIndex,
|
||||
ASTextNodeRendererMeasureOptionBlock uses the cap height option to generate each glyph index, but combines all but the
|
||||
first and last line rect into a single block. Looks nice for multiline selection.
|
||||
*/
|
||||
typedef NS_ENUM(NSUInteger, CKTextKitRendererMeasureOption) {
|
||||
CKTextKitRendererMeasureOptionLineHeight,
|
||||
CKTextKitRendererMeasureOptionCapHeight,
|
||||
CKTextKitRendererMeasureOptionBlock
|
||||
typedef NS_ENUM(NSUInteger, ASTextKitRendererMeasureOption) {
|
||||
ASTextKitRendererMeasureOptionLineHeight,
|
||||
ASTextKitRendererMeasureOptionCapHeight,
|
||||
ASTextKitRendererMeasureOptionBlock
|
||||
};
|
||||
|
||||
@interface CKTextKitRenderer (Positioning)
|
||||
@interface ASTextKitRenderer (Positioning)
|
||||
|
||||
/**
|
||||
Returns the bounding rect for the given character range.
|
||||
@@ -48,7 +48,7 @@ typedef NS_ENUM(NSUInteger, CKTextKitRendererMeasureOption) {
|
||||
|
||||
@param textRange The character range for which the rects will be computed. Should be within the range of the
|
||||
attributedString of this renderer.
|
||||
@param measureOption The measure option to use for construction of the rects. See CKTextKitRendererMeasureOption
|
||||
@param measureOption The measure option to use for construction of the rects. See ASTextKitRendererMeasureOption
|
||||
docs for usage.
|
||||
|
||||
@discussion This method is useful for providing highlighting text. Returned rects are in the coordinate space of the
|
||||
@@ -57,7 +57,7 @@ typedef NS_ENUM(NSUInteger, CKTextKitRendererMeasureOption) {
|
||||
Triggers initialization of textkit components, truncation, and sizing.
|
||||
*/
|
||||
- (NSArray *)rectsForTextRange:(NSRange)textRange
|
||||
measureOption:(CKTextKitRendererMeasureOption)measureOption;
|
||||
measureOption:(ASTextKitRendererMeasureOption)measureOption;
|
||||
|
||||
/**
|
||||
Enumerate the text character indexes at a position within the coordinate space of the renderer.
|
||||
@@ -8,22 +8,22 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#import "CKTextKitRenderer+Positioning.h"
|
||||
#import "ASTextKitRenderer+Positioning.h"
|
||||
|
||||
#import <CoreText/CoreText.h>
|
||||
|
||||
#import "ASAssert.h"
|
||||
|
||||
#import "CKTextKitContext.h"
|
||||
#import "CKTextKitShadower.h"
|
||||
#import "ASTextKitContext.h"
|
||||
#import "ASTextKitShadower.h"
|
||||
|
||||
static const CGFloat CKTextKitRendererGlyphTouchHitSlop = 5.0;
|
||||
static const CGFloat CKTextKitRendererTextCapHeightPadding = 1.3;
|
||||
static const CGFloat ASTextKitRendererGlyphTouchHitSlop = 5.0;
|
||||
static const CGFloat ASTextKitRendererTextCapHeightPadding = 1.3;
|
||||
|
||||
@implementation CKTextKitRenderer (Tracking)
|
||||
@implementation ASTextKitRenderer (Tracking)
|
||||
|
||||
- (NSArray *)rectsForTextRange:(NSRange)textRange
|
||||
measureOption:(CKTextKitRendererMeasureOption)measureOption
|
||||
measureOption:(ASTextKitRendererMeasureOption)measureOption
|
||||
{
|
||||
__block NSArray *textRects = @[];
|
||||
[self.context performBlockWithLockedTextKitComponents:^(NSLayoutManager *layoutManager, NSTextStorage *textStorage, NSTextContainer *textContainer) {
|
||||
@@ -75,7 +75,7 @@ static const CGFloat CKTextKitRendererTextCapHeightPadding = 1.3;
|
||||
}
|
||||
|
||||
if (!CGRectIsNull(lineRect)) {
|
||||
if (measureOption == CKTextKitRendererMeasureOptionBlock) {
|
||||
if (measureOption == ASTextKitRendererMeasureOptionBlock) {
|
||||
// For the block measurement option we store the first & last rect as
|
||||
// special cases, then merge everything else into a single block rect
|
||||
if (CGRectIsNull(firstRect)) {
|
||||
@@ -105,7 +105,7 @@ static const CGFloat CKTextKitRendererTextCapHeightPadding = 1.3;
|
||||
}
|
||||
}];
|
||||
|
||||
if (measureOption == CKTextKitRendererMeasureOptionBlock) {
|
||||
if (measureOption == ASTextKitRendererMeasureOptionBlock) {
|
||||
// Block measure option is handled differently with just 3 vars for the entire range.
|
||||
if (!CGRectIsNull(firstRect)) {
|
||||
if (!CGRectIsNull(blockRect)) {
|
||||
@@ -170,7 +170,7 @@ static const CGFloat CKTextKitRendererTextCapHeightPadding = 1.3;
|
||||
as an approximation to work around problems in TextKit's glyph sizing.
|
||||
*/
|
||||
- (CGRect)_internalRectForGlyphAtIndex:(NSUInteger)glyphIndex
|
||||
measureOption:(CKTextKitRendererMeasureOption)measureOption
|
||||
measureOption:(ASTextKitRendererMeasureOption)measureOption
|
||||
layoutManager:(NSLayoutManager *)layoutManager
|
||||
textContainer:(NSTextContainer *)textContainer
|
||||
textStorage:(NSTextStorage *)textStorage
|
||||
@@ -226,8 +226,8 @@ static const CGFloat CKTextKitRendererTextCapHeightPadding = 1.3;
|
||||
CGPoint glyphCenter = CGPointMake(CGRectGetMidX(glyphRect), CGRectGetMidY(glyphRect));
|
||||
|
||||
CGRect properGlyphRect;
|
||||
if (measureOption == CKTextKitRendererMeasureOptionCapHeight
|
||||
|| measureOption == CKTextKitRendererMeasureOptionBlock) {
|
||||
if (measureOption == ASTextKitRendererMeasureOptionCapHeight
|
||||
|| measureOption == ASTextKitRendererMeasureOptionBlock) {
|
||||
CGFloat ascent = CTFontGetAscent(font);
|
||||
CGFloat descent = CTFontGetDescent(font);
|
||||
CGFloat capHeight = CTFontGetCapHeight(font);
|
||||
@@ -237,7 +237,7 @@ static const CGFloat CKTextKitRendererTextCapHeightPadding = 1.3;
|
||||
// For visual balance, we add the cap height padding above the cap, and
|
||||
// below the baseline, we scale by the descent so it grows with the size of
|
||||
// the text.
|
||||
CGFloat topPadding = CKTextKitRendererTextCapHeightPadding * descent;
|
||||
CGFloat topPadding = ASTextKitRendererTextCapHeightPadding * descent;
|
||||
CGFloat bottomPadding = topPadding;
|
||||
|
||||
properGlyphRect = CGRectMake(glyphCenter.x - advance * 0.5,
|
||||
@@ -263,7 +263,7 @@ static const CGFloat CKTextKitRendererTextCapHeightPadding = 1.3;
|
||||
// This method is a little complex because it has to call out to client code from inside an enumeration that needs
|
||||
// to achieve a lock on the textkit components. It cannot call out to client code from within that lock so we just
|
||||
// perform the textkit-locked ops inside the locked context.
|
||||
CKTextKitContext *lockingContext = self.context;
|
||||
ASTextKitContext *lockingContext = self.context;
|
||||
CGPoint internalPosition = [self.shadower offsetPointWithExternalPoint:externalPosition];
|
||||
__block BOOL invalidPosition = NO;
|
||||
[lockingContext performBlockWithLockedTextKitComponents:^(NSLayoutManager *layoutManager, NSTextStorage *textStorage, NSTextContainer *textContainer) {
|
||||
@@ -314,7 +314,7 @@ static const CGFloat CKTextKitRendererTextCapHeightPadding = 1.3;
|
||||
characterIndex = [layoutManager characterIndexForGlyphAtIndex:glyphIndex];
|
||||
|
||||
glyphRect = [self _internalRectForGlyphAtIndex:glyphIndex
|
||||
measureOption:CKTextKitRendererMeasureOptionLineHeight
|
||||
measureOption:ASTextKitRendererMeasureOptionLineHeight
|
||||
layoutManager:layoutManager
|
||||
textContainer:textContainer
|
||||
textStorage:textStorage];
|
||||
@@ -322,7 +322,7 @@ static const CGFloat CKTextKitRendererTextCapHeightPadding = 1.3;
|
||||
|
||||
// Sometimes TextKit plays jokes on us and returns glyphs that really aren't close to the point in question.
|
||||
// Silly TextKit...
|
||||
if (!isValidGlyph || !CGRectContainsPoint(CGRectInset(glyphRect, -CKTextKitRendererGlyphTouchHitSlop, -CKTextKitRendererGlyphTouchHitSlop), currentPoint)) {
|
||||
if (!isValidGlyph || !CGRectContainsPoint(CGRectInset(glyphRect, -ASTextKitRendererGlyphTouchHitSlop, -ASTextKitRendererGlyphTouchHitSlop), currentPoint)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -343,7 +343,7 @@ static const CGFloat CKTextKitRendererTextCapHeightPadding = 1.3;
|
||||
}
|
||||
|
||||
// Take everything after our final character as trailing space.
|
||||
NSArray *finalRects = [self rectsForTextRange:NSMakeRange([textStorage length] - 1, 1) measureOption:CKTextKitRendererMeasureOptionLineHeight];
|
||||
NSArray *finalRects = [self rectsForTextRange:NSMakeRange([textStorage length] - 1, 1) measureOption:ASTextKitRendererMeasureOptionLineHeight];
|
||||
CGRect finalGlyphRect = [[finalRects lastObject] CGRectValue];
|
||||
CGPoint origin = CGPointMake(CGRectGetMaxX(finalGlyphRect), CGRectGetMinY(finalGlyphRect));
|
||||
CGSize size = CGSizeMake(calculatedSize.width - origin.x, calculatedSize.height - origin.y);
|
||||
@@ -8,21 +8,21 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#import "CKTextKitRenderer.h"
|
||||
#import "ASTextKitRenderer.h"
|
||||
|
||||
/**
|
||||
Application extensions to NSTextCheckingType. We're allowed to do this (see NSTextCheckingAllCustomTypes).
|
||||
*/
|
||||
static uint64_t const CKTextKitTextCheckingTypeEntity = 1ULL << 33;
|
||||
static uint64_t const CKTextKitTextCheckingTypeTruncation = 1ULL << 34;
|
||||
static uint64_t const ASTextKitTextCheckingTypeEntity = 1ULL << 33;
|
||||
static uint64_t const ASTextKitTextCheckingTypeTruncation = 1ULL << 34;
|
||||
|
||||
@class CKTextKitEntityAttribute;
|
||||
@class ASTextKitEntityAttribute;
|
||||
|
||||
@interface CKTextKitTextCheckingResult : NSTextCheckingResult
|
||||
@property (nonatomic, strong, readonly) CKTextKitEntityAttribute *entityAttribute;
|
||||
@interface ASTextKitTextCheckingResult : NSTextCheckingResult
|
||||
@property (nonatomic, strong, readonly) ASTextKitEntityAttribute *entityAttribute;
|
||||
@end
|
||||
|
||||
@interface CKTextKitRenderer (TextChecking)
|
||||
@interface ASTextKitRenderer (TextChecking)
|
||||
|
||||
- (NSTextCheckingResult *)textCheckingResultAtPoint:(CGPoint)point;
|
||||
|
||||
@@ -8,14 +8,14 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#import "CKTextKitRenderer+TextChecking.h"
|
||||
#import "ASTextKitRenderer+TextChecking.h"
|
||||
|
||||
#import "CKTextKitAttributes.h"
|
||||
#import "CKTextKitEntityAttribute.h"
|
||||
#import "CKTextKitRenderer+Positioning.h"
|
||||
#import "CKTextKitTailTruncater.h"
|
||||
#import "ASTextKitAttributes.h"
|
||||
#import "ASTextKitEntityAttribute.h"
|
||||
#import "ASTextKitRenderer+Positioning.h"
|
||||
#import "ASTextKitTailTruncater.h"
|
||||
|
||||
@implementation CKTextKitTextCheckingResult
|
||||
@implementation ASTextKitTextCheckingResult
|
||||
|
||||
{
|
||||
// Be explicit about the fact that we are overriding the super class' implementation of -range and -resultType
|
||||
@@ -27,7 +27,7 @@
|
||||
}
|
||||
|
||||
- (instancetype)initWithType:(NSTextCheckingType)type
|
||||
entityAttribute:(CKTextKitEntityAttribute *)entityAttribute
|
||||
entityAttribute:(ASTextKitEntityAttribute *)entityAttribute
|
||||
range:(NSRange)range
|
||||
{
|
||||
if ((self = [super init])) {
|
||||
@@ -50,7 +50,7 @@
|
||||
|
||||
@end
|
||||
|
||||
@implementation CKTextKitRenderer (TextChecking)
|
||||
@implementation ASTextKitRenderer (TextChecking)
|
||||
|
||||
- (NSTextCheckingResult *)textCheckingResultAtPoint:(CGPoint)point
|
||||
{
|
||||
@@ -62,7 +62,7 @@
|
||||
NSRange visibleRange = self.truncater.visibleRanges[0];
|
||||
__block NSRange truncationTokenRange = { NSNotFound, 0 };
|
||||
|
||||
[truncationAttributedString enumerateAttribute:CKTextKitTruncationAttributeName inRange:NSMakeRange(0, truncationAttributedString.length)
|
||||
[truncationAttributedString enumerateAttribute:ASTextKitTruncationAttributeName inRange:NSMakeRange(0, truncationAttributedString.length)
|
||||
options:0
|
||||
usingBlock:^(id value, NSRange range, BOOL *stop) {
|
||||
if (value != nil && range.length > 0) {
|
||||
@@ -79,15 +79,15 @@
|
||||
|
||||
[self enumerateTextIndexesAtPosition:point usingBlock:^(NSUInteger index, CGRect glyphBoundingRect, BOOL *stop){
|
||||
if (index >= truncationTokenRange.location) {
|
||||
result = [[CKTextKitTextCheckingResult alloc] initWithType:CKTextKitTextCheckingTypeTruncation
|
||||
result = [[ASTextKitTextCheckingResult alloc] initWithType:ASTextKitTextCheckingTypeTruncation
|
||||
entityAttribute:nil
|
||||
range:truncationTokenRange];
|
||||
} else {
|
||||
NSRange range;
|
||||
NSDictionary *attributes = [attributedString attributesAtIndex:index effectiveRange:&range];
|
||||
CKTextKitEntityAttribute *entityAttribute = attributes[CKTextKitEntityAttributeName];
|
||||
ASTextKitEntityAttribute *entityAttribute = attributes[ASTextKitEntityAttributeName];
|
||||
if (entityAttribute) {
|
||||
result = [[CKTextKitTextCheckingResult alloc] initWithType:CKTextKitTextCheckingTypeEntity
|
||||
result = [[ASTextKitTextCheckingResult alloc] initWithType:ASTextKitTextCheckingTypeEntity
|
||||
entityAttribute:entityAttribute
|
||||
range:range];
|
||||
}
|
||||
@@ -12,14 +12,14 @@
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import "CKTextKitAttributes.h"
|
||||
#import "ASTextKitAttributes.h"
|
||||
|
||||
@class CKTextKitContext;
|
||||
@class CKTextKitShadower;
|
||||
@protocol CKTextKitTruncating;
|
||||
@class ASTextKitContext;
|
||||
@class ASTextKitShadower;
|
||||
@protocol ASTextKitTruncating;
|
||||
|
||||
/**
|
||||
CKTextKitRenderer is a modular object that is responsible for laying out and drawing text.
|
||||
ASTextKitRenderer is a modular object that is responsible for laying out and drawing text.
|
||||
|
||||
A renderer will hold onto the TextKit layouts for the given attributes after initialization. This may constitute a
|
||||
large amount of memory for large enough applications, so care must be taken when keeping many of these around in-memory
|
||||
@@ -33,23 +33,23 @@
|
||||
coordinate space. Padding will be added for you in order to ensure clipping does not occur, and additional information
|
||||
on this transform is available via the shadower should you need it.
|
||||
*/
|
||||
@interface CKTextKitRenderer : NSObject
|
||||
@interface ASTextKitRenderer : NSObject
|
||||
|
||||
/**
|
||||
Designated Initializer
|
||||
dvlkferufedgjnhjjfhldjedlunvtdtv
|
||||
@discussion Sizing will occur as a result of initialization, so be careful when/where you use this.
|
||||
*/
|
||||
- (instancetype)initWithTextKitAttributes:(const CKTextKitAttributes &)textComponentAttributes
|
||||
- (instancetype)initWithTextKitAttributes:(const ASTextKitAttributes &)textComponentAttributes
|
||||
constrainedSize:(const CGSize)constrainedSize;
|
||||
|
||||
@property (nonatomic, strong, readonly) CKTextKitContext *context;
|
||||
@property (nonatomic, strong, readonly) ASTextKitContext *context;
|
||||
|
||||
@property (nonatomic, strong, readonly) id<CKTextKitTruncating> truncater;
|
||||
@property (nonatomic, strong, readonly) id<ASTextKitTruncating> truncater;
|
||||
|
||||
@property (nonatomic, strong, readonly) CKTextKitShadower *shadower;
|
||||
@property (nonatomic, strong, readonly) ASTextKitShadower *shadower;
|
||||
|
||||
@property (nonatomic, assign, readonly) CKTextKitAttributes attributes;
|
||||
@property (nonatomic, assign, readonly) ASTextKitAttributes attributes;
|
||||
|
||||
@property (nonatomic, assign, readonly) CGSize constrainedSize;
|
||||
|
||||
@@ -8,14 +8,14 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#import "CKTextKitRenderer.h"
|
||||
#import "ASTextKitRenderer.h"
|
||||
|
||||
#import "ASAssert.h"
|
||||
|
||||
#import "CKTextKitContext.h"
|
||||
#import "CKTextKitShadower.h"
|
||||
#import "CKTextKitTailTruncater.h"
|
||||
#import "CKTextKitTruncating.h"
|
||||
#import "ASTextKitContext.h"
|
||||
#import "ASTextKitShadower.h"
|
||||
#import "ASTextKitTailTruncater.h"
|
||||
#import "ASTextKitTruncating.h"
|
||||
|
||||
static NSCharacterSet *_defaultAvoidTruncationCharacterSet()
|
||||
{
|
||||
@@ -30,20 +30,20 @@ static NSCharacterSet *_defaultAvoidTruncationCharacterSet()
|
||||
return truncationCharacterSet;
|
||||
}
|
||||
|
||||
@implementation CKTextKitRenderer {
|
||||
@implementation ASTextKitRenderer {
|
||||
CGSize _calculatedSize;
|
||||
}
|
||||
|
||||
#pragma mark - Initialization
|
||||
|
||||
- (instancetype)initWithTextKitAttributes:(const CKTextKitAttributes &)attributes
|
||||
- (instancetype)initWithTextKitAttributes:(const ASTextKitAttributes &)attributes
|
||||
constrainedSize:(const CGSize)constrainedSize
|
||||
{
|
||||
if (self = [super init]) {
|
||||
_constrainedSize = constrainedSize;
|
||||
_attributes = attributes;
|
||||
|
||||
_shadower = [[CKTextKitShadower alloc] initWithShadowOffset:attributes.shadowOffset
|
||||
_shadower = [[ASTextKitShadower alloc] initWithShadowOffset:attributes.shadowOffset
|
||||
shadowColor:attributes.shadowColor
|
||||
shadowOpacity:attributes.shadowOpacity
|
||||
shadowRadius:attributes.shadowRadius];
|
||||
@@ -51,14 +51,14 @@ static NSCharacterSet *_defaultAvoidTruncationCharacterSet()
|
||||
// We must inset the constrained size by the size of the shadower.
|
||||
CGSize shadowConstrainedSize = [_shadower insetSizeWithConstrainedSize:_constrainedSize];
|
||||
|
||||
_context = [[CKTextKitContext alloc] initWithAttributedString:attributes.attributedString
|
||||
_context = [[ASTextKitContext alloc] initWithAttributedString:attributes.attributedString
|
||||
lineBreakMode:attributes.lineBreakMode
|
||||
maximumNumberOfLines:attributes.maximumNumberOfLines
|
||||
exclusionPaths:attributes.exclusionPaths
|
||||
constrainedSize:shadowConstrainedSize
|
||||
layoutManagerFactory:attributes.layoutManagerFactory];
|
||||
|
||||
_truncater = [[CKTextKitTailTruncater alloc] initWithContext:_context
|
||||
_truncater = [[ASTextKitTailTruncater alloc] initWithContext:_context
|
||||
truncationAttributedString:attributes.truncationAttributedString
|
||||
avoidTailTruncationSet:attributes.avoidTailTruncationSet ?: _defaultAvoidTruncationCharacterSet()
|
||||
constrainedSize:shadowConstrainedSize];
|
||||
@@ -13,7 +13,7 @@
|
||||
/**
|
||||
* @abstract an immutable class for calculating shadow padding drawing a shadowed background for text
|
||||
*/
|
||||
@interface CKTextKitShadower : NSObject
|
||||
@interface ASTextKitShadower : NSObject
|
||||
|
||||
- (instancetype)initWithShadowOffset:(CGSize)shadowOffset
|
||||
shadowColor:(UIColor *)shadowColor
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#import "CKTextKitShadower.h"
|
||||
#import "ASTextKitShadower.h"
|
||||
|
||||
static inline CGSize _insetSize(CGSize size, UIEdgeInsets insets)
|
||||
{
|
||||
@@ -25,7 +25,7 @@ static inline UIEdgeInsets _invertInsets(UIEdgeInsets insets)
|
||||
};
|
||||
}
|
||||
|
||||
@implementation CKTextKitShadower {
|
||||
@implementation ASTextKitShadower {
|
||||
UIEdgeInsets _calculatedShadowPadding;
|
||||
}
|
||||
|
||||
@@ -10,8 +10,8 @@
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import "CKTextKitTruncating.h"
|
||||
#import "ASTextKitTruncating.h"
|
||||
|
||||
@interface CKTextKitTailTruncater : NSObject <CKTextKitTruncating>
|
||||
@interface ASTextKitTailTruncater : NSObject <ASTextKitTruncating>
|
||||
|
||||
@end
|
||||
@@ -10,12 +10,12 @@
|
||||
|
||||
#import "ASAssert.h"
|
||||
|
||||
#import "CKTextKitContext.h"
|
||||
#import "CKTextKitTailTruncater.h"
|
||||
#import "ASTextKitContext.h"
|
||||
#import "ASTextKitTailTruncater.h"
|
||||
|
||||
@implementation CKTextKitTailTruncater
|
||||
@implementation ASTextKitTailTruncater
|
||||
{
|
||||
__weak CKTextKitContext *_context;
|
||||
__weak ASTextKitContext *_context;
|
||||
NSAttributedString *_truncationAttributedString;
|
||||
NSCharacterSet *_avoidTailTruncationSet;
|
||||
CGSize _constrainedSize;
|
||||
@@ -23,7 +23,7 @@
|
||||
@synthesize visibleRanges = _visibleRanges;
|
||||
@synthesize truncationStringRect = _truncationStringRect;
|
||||
|
||||
- (instancetype)initWithContext:(CKTextKitContext *)context
|
||||
- (instancetype)initWithContext:(ASTextKitContext *)context
|
||||
truncationAttributedString:(NSAttributedString *)truncationAttributedString
|
||||
avoidTailTruncationSet:(NSCharacterSet *)avoidTailTruncationSet
|
||||
constrainedSize:(CGSize)constrainedSize
|
||||
@@ -69,7 +69,7 @@
|
||||
BOOL leftAligned = CGRectGetMinX(lastLineRect) == CGRectGetMinX(lastLineUsedRect) || !rtlWritingDirection;
|
||||
|
||||
// Calculate the bounding rectangle for the truncation message
|
||||
CKTextKitContext *truncationContext = [[CKTextKitContext alloc] initWithAttributedString:_truncationAttributedString
|
||||
ASTextKitContext *truncationContext = [[ASTextKitContext alloc] initWithAttributedString:_truncationAttributedString
|
||||
lineBreakMode:NSLineBreakByWordWrapping
|
||||
maximumNumberOfLines:1
|
||||
exclusionPaths:nil
|
||||
@@ -12,9 +12,9 @@
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import "CKTextKitRenderer.h"
|
||||
#import "ASTextKitRenderer.h"
|
||||
|
||||
@protocol CKTextKitTruncating <NSObject>
|
||||
@protocol ASTextKitTruncating <NSObject>
|
||||
|
||||
@property (nonatomic, assign, readonly) std::vector<NSRange> visibleRanges;
|
||||
@property (nonatomic, assign, readonly) CGRect truncationStringRect;
|
||||
@@ -29,7 +29,7 @@
|
||||
|
||||
The truncater should not store a strong reference to the context to prevent retain cycles.
|
||||
*/
|
||||
- (instancetype)initWithContext:(CKTextKitContext *)context
|
||||
- (instancetype)initWithContext:(ASTextKitContext *)context
|
||||
truncationAttributedString:(NSAttributedString *)truncationAttributedString
|
||||
avoidTailTruncationSet:(NSCharacterSet *)avoidTailTruncationSet
|
||||
constrainedSize:(CGSize)constrainedSize;
|
||||
12
AsyncDisplayKit/TextKit/ASTextNodeTypes.h
Normal file
12
AsyncDisplayKit/TextKit/ASTextNodeTypes.h
Normal file
@@ -0,0 +1,12 @@
|
||||
/* Copyright (c) 2014-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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
// Use this attribute name to add "word kerning"
|
||||
static NSString *const ASTextNodeWordKerningAttributeName = @"ASAttributedStringWordKerning";
|
||||
30
AsyncDisplayKit/TextKit/ASTextNodeWordKerner.h
Normal file
30
AsyncDisplayKit/TextKit/ASTextNodeWordKerner.h
Normal file
@@ -0,0 +1,30 @@
|
||||
/* Copyright (c) 2014-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 <Foundation/Foundation.h>
|
||||
#import <UIKit/NSLayoutManager.h>
|
||||
|
||||
|
||||
/**
|
||||
@abstract This class acts as the NSLayoutManagerDelegate for ASTextNode.
|
||||
@discussion Its current job is word kerning, i.e. adjusting the width of spaces to match the set
|
||||
wordKernedSpaceWidth. If word kerning is not needed, set the layoutManager's delegate to nil.
|
||||
*/
|
||||
@interface ASTextNodeWordKerner : NSObject <NSLayoutManagerDelegate>
|
||||
|
||||
/**
|
||||
The following @optional NSLayoutManagerDelegate methods are implemented:
|
||||
|
||||
- (NSUInteger)layoutManager:(NSLayoutManager *)layoutManager shouldGenerateGlyphs:(const CGGlyph *)glyphs properties:(const NSGlyphProperty *)props characterIndexes:(const NSUInteger *)charIndexes font:(UIFont *)aFont forGlyphRange:(NSRange)glyphRange NS_AVAILABLE_IOS(7_0);
|
||||
|
||||
- (NSControlCharacterAction)layoutManager:(NSLayoutManager *)layoutManager shouldUseAction:(NSControlCharacterAction)action forControlCharacterAtIndex:(NSUInteger)charIndex NS_AVAILABLE_IOS(7_0);
|
||||
|
||||
- (CGRect)layoutManager:(NSLayoutManager *)layoutManager boundingBoxForControlGlyphAtIndex:(NSUInteger)glyphIndex forTextContainer:(NSTextContainer *)textContainer proposedLineFragment:(CGRect)proposedRect glyphPosition:(CGPoint)glyphPosition characterIndex:(NSUInteger)charIndex NS_AVAILABLE_IOS(7_0);
|
||||
*/
|
||||
|
||||
@end
|
||||
129
AsyncDisplayKit/TextKit/ASTextNodeWordKerner.m
Normal file
129
AsyncDisplayKit/TextKit/ASTextNodeWordKerner.m
Normal file
@@ -0,0 +1,129 @@
|
||||
/* Copyright (c) 2014-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 "ASTextNodeWordKerner.h"
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import "ASTextNodeTypes.h"
|
||||
|
||||
@implementation ASTextNodeWordKerner
|
||||
|
||||
#pragma mark - NSLayoutManager Delegate
|
||||
- (NSUInteger)layoutManager:(NSLayoutManager *)layoutManager shouldGenerateGlyphs:(const CGGlyph *)glyphs properties:(const NSGlyphProperty *)properties characterIndexes:(const NSUInteger *)characterIndexes font:(UIFont *)aFont forGlyphRange:(NSRange)glyphRange
|
||||
{
|
||||
NSUInteger glyphCount = glyphRange.length;
|
||||
NSGlyphProperty *newGlyphProperties = NULL;
|
||||
|
||||
BOOL usesWordKerning = NO;
|
||||
|
||||
// If our typing attributes specify word kerning, specify the spaces as whitespace control characters so we can customize their width.
|
||||
// Are any of the characters spaces?
|
||||
NSString *textStorageString = layoutManager.textStorage.string;
|
||||
for (NSUInteger arrayIndex = 0; arrayIndex < glyphCount; arrayIndex++) {
|
||||
NSUInteger characterIndex = characterIndexes[arrayIndex];
|
||||
if ([textStorageString characterAtIndex:characterIndex] != ' ')
|
||||
continue;
|
||||
|
||||
// If we've set the whitespace control character for this space already, we have nothing to do.
|
||||
if (properties[arrayIndex] == NSGlyphPropertyControlCharacter) {
|
||||
usesWordKerning = YES;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Create new glyph properties, if necessary.
|
||||
if (!newGlyphProperties) {
|
||||
newGlyphProperties = (NSGlyphProperty *)malloc(sizeof(NSGlyphProperty) * glyphCount);
|
||||
memcpy(newGlyphProperties, properties, (sizeof(NSGlyphProperty) * glyphCount));
|
||||
}
|
||||
|
||||
// It's a space. Make it a whitespace control character.
|
||||
newGlyphProperties[arrayIndex] = NSGlyphPropertyControlCharacter;
|
||||
}
|
||||
|
||||
// If we don't have any custom glyph properties, return 0 to indicate to the layout manager that it should use the standard glyphs+properties.
|
||||
if (!newGlyphProperties) {
|
||||
if (usesWordKerning) {
|
||||
// If the text does use word kerning we have to make sure we return the correct glyphCount, or the layout manager will just use the default properties and ignore our kerning.
|
||||
[layoutManager setGlyphs:glyphs properties:properties characterIndexes:characterIndexes font:aFont forGlyphRange:glyphRange];
|
||||
return glyphCount;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, use our custom glyph properties.
|
||||
[layoutManager setGlyphs:glyphs properties:newGlyphProperties characterIndexes:characterIndexes font:aFont forGlyphRange:glyphRange];
|
||||
free(newGlyphProperties);
|
||||
|
||||
return glyphCount;
|
||||
}
|
||||
|
||||
- (NSControlCharacterAction)layoutManager:(NSLayoutManager *)layoutManager shouldUseAction:(NSControlCharacterAction)defaultAction forControlCharacterAtIndex:(NSUInteger)characterIndex
|
||||
{
|
||||
// If it's a space character and we have custom word kerning, use the whitespace action control character.
|
||||
if ([layoutManager.textStorage.string characterAtIndex:characterIndex] == ' ')
|
||||
return NSControlCharacterWhitespaceAction;
|
||||
|
||||
return defaultAction;
|
||||
}
|
||||
|
||||
- (CGRect)layoutManager:(NSLayoutManager *)layoutManager boundingBoxForControlGlyphAtIndex:(NSUInteger)glyphIndex forTextContainer:(NSTextContainer *)textContainer proposedLineFragment:(CGRect)proposedRect glyphPosition:(CGPoint)glyphPosition characterIndex:(NSUInteger)characterIndex
|
||||
{
|
||||
CGFloat wordKernedSpaceWidth = [self _wordKernedSpaceWidthForCharacterAtIndex:characterIndex atGlyphPosition:glyphPosition forTextContainer:textContainer layoutManager:layoutManager];
|
||||
return CGRectMake(glyphPosition.x, glyphPosition.y, wordKernedSpaceWidth, CGRectGetHeight(proposedRect));
|
||||
}
|
||||
|
||||
- (CGFloat)_wordKernedSpaceWidthForCharacterAtIndex:(NSUInteger)characterIndex atGlyphPosition:(CGPoint)glyphPosition forTextContainer:(NSTextContainer *)textContainer layoutManager:(NSLayoutManager *)layoutManager
|
||||
{
|
||||
// We use a map table for pointer equality and non-copying keys.
|
||||
static NSMapTable *spaceSizes;
|
||||
// NSMapTable is a defined thread unsafe class, so we need to synchronize
|
||||
// access in a light manner. So we use dispatch_sync on this queue for all
|
||||
// access to the map table.
|
||||
static dispatch_queue_t mapQueue;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
spaceSizes = [[NSMapTable alloc] initWithKeyOptions:NSMapTableStrongMemory valueOptions:NSMapTableStrongMemory capacity:1];
|
||||
mapQueue = dispatch_queue_create("org.AsyncDisplayKit.wordKerningQueue", DISPATCH_QUEUE_SERIAL);
|
||||
});
|
||||
CGFloat ordinarySpaceWidth;
|
||||
UIFont *font = [layoutManager.textStorage attribute:NSFontAttributeName atIndex:characterIndex effectiveRange:NULL];
|
||||
CGFloat wordKerning = [[layoutManager.textStorage attribute:ASTextNodeWordKerningAttributeName atIndex:characterIndex effectiveRange:NULL] floatValue];
|
||||
__block NSNumber *ordinarySpaceSizeValue;
|
||||
dispatch_sync(mapQueue, ^{
|
||||
ordinarySpaceSizeValue = [spaceSizes objectForKey:font];
|
||||
});
|
||||
if (ordinarySpaceSizeValue == nil) {
|
||||
ordinarySpaceWidth = [@" " sizeWithAttributes:@{ NSFontAttributeName : font }].width;
|
||||
dispatch_async(mapQueue, ^{
|
||||
[spaceSizes setObject:@(ordinarySpaceWidth) forKey:font];
|
||||
});
|
||||
} else {
|
||||
ordinarySpaceWidth = [ordinarySpaceSizeValue floatValue];
|
||||
}
|
||||
|
||||
CGFloat totalKernedWidth = (ordinarySpaceWidth + wordKerning);
|
||||
|
||||
// TextKit normally handles whitespace by increasing the advance of the previous glyph, rather than displaying an
|
||||
// actual glyph for the whitespace itself. However, in order to implement word kerning, we explicitly require a
|
||||
// discrete glyph whose bounding box we can specify. The problem is that TextKit does not know this glyph is
|
||||
// invisible. From TextKit's perspective, this whitespace glyph is a glyph that MUST be displayed. Thus when it
|
||||
// comes to determining linebreaks, the width of this trailing whitespace glyph is considered. This causes
|
||||
// our text to wrap sooner than it otherwise would, as room is allocated at the end of each line for a glyph that
|
||||
// isn't actually visible. To implement our desired behavior, we check to see if the current whitespace glyph
|
||||
// would break to the next line. If it breaks to the next line, then this constitutes trailing whitespace, and
|
||||
// we specify enough room to fill up the remainder of the line, but nothing more.
|
||||
if (glyphPosition.x + totalKernedWidth > textContainer.size.width) {
|
||||
return (textContainer.size.width - glyphPosition.x);
|
||||
}
|
||||
|
||||
return totalKernedWidth;
|
||||
}
|
||||
|
||||
@end
|
||||
Reference in New Issue
Block a user