You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
678 lines
23 KiB
678 lines
23 KiB
// |
|
// TYTextContainer.m |
|
// TYAttributedLabelDemo |
|
// |
|
// Created by tanyang on 15/6/4. |
|
// Copyright (c) 2015年 tanyang. All rights reserved. |
|
// |
|
|
|
#import "TYTextContainer.h" |
|
|
|
#define kTextColor [UIColor colorWithRed:51/255.0 green:51/255.0 blue:51/255.0 alpha:1] |
|
#define kLinkColor [UIColor colorWithRed:0/255.0 green:91/255.0 blue:255/255.0 alpha:1] |
|
|
|
// this code quote TTTAttributedLabel |
|
static inline CGSize CTFramesetterSuggestFrameSizeForAttributedStringWithConstraints(CTFramesetterRef framesetter, NSAttributedString *attributedString, CGSize size, NSUInteger numberOfLines) { |
|
CFRange rangeToSize = CFRangeMake(0, (CFIndex)[attributedString length]); |
|
CGSize constraints = CGSizeMake(size.width, MAXFLOAT); |
|
|
|
if (numberOfLines > 0) { |
|
// If the line count of the label more than 1, limit the range to size to the number of lines that have been set |
|
CGMutablePathRef path = CGPathCreateMutable(); |
|
CGPathAddRect(path, NULL, CGRectMake(0.0f, 0.0f, constraints.width, MAXFLOAT)); |
|
CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), path, NULL); |
|
CFArrayRef lines = CTFrameGetLines(frame); |
|
|
|
if (CFArrayGetCount(lines) > 0) { |
|
NSInteger lastVisibleLineIndex = MIN((CFIndex)numberOfLines, CFArrayGetCount(lines)) - 1; |
|
CTLineRef lastVisibleLine = CFArrayGetValueAtIndex(lines, lastVisibleLineIndex); |
|
|
|
CFRange rangeToLayout = CTLineGetStringRange(lastVisibleLine); |
|
rangeToSize = CFRangeMake(0, rangeToLayout.location + rangeToLayout.length); |
|
} |
|
|
|
CFRelease(frame); |
|
CFRelease(path); |
|
} |
|
|
|
CGSize suggestedSize = CTFramesetterSuggestFrameSizeWithConstraints(framesetter, rangeToSize, NULL, constraints, NULL); |
|
|
|
return CGSizeMake(ceil(suggestedSize.width), ceil(suggestedSize.height)); |
|
} |
|
|
|
@interface TYTextContainer() |
|
@property (nonatomic, strong) NSMutableArray *textStorageArray; // run数组 |
|
@property (nonatomic, strong) NSArray *textStorages; // run array copy |
|
|
|
@property (nonatomic, strong) NSDictionary *drawRectDictionary; |
|
@property (nonatomic, strong) NSDictionary *runRectDictionary; // runRect字典 |
|
@property (nonatomic, strong) NSDictionary *linkRectDictionary; // linkRect字典 |
|
|
|
@property (nonatomic, assign) NSInteger replaceStringNum; // 图片替换字符数 |
|
@property (nonatomic, strong) NSMutableAttributedString *attString; |
|
@property (nonatomic, assign) CTFrameRef frameRef; |
|
@property (nonatomic, assign) CGFloat textHeight; |
|
@property (nonatomic, assign) CGFloat textWidth; |
|
|
|
@end |
|
|
|
@implementation TYTextContainer |
|
|
|
- (instancetype)init |
|
{ |
|
if (self = [super init]) { |
|
[self setupProperty]; |
|
} |
|
return self; |
|
} |
|
|
|
#pragma mark - getter |
|
|
|
- (NSMutableArray *)textStorageArray |
|
{ |
|
if (_textStorageArray == nil) { |
|
_textStorageArray = [NSMutableArray array]; |
|
} |
|
return _textStorageArray; |
|
} |
|
|
|
- (NSString *)text{ |
|
return _attString.string; |
|
} |
|
|
|
- (NSAttributedString *)attributedText |
|
{ |
|
return [_attString copy]; |
|
} |
|
|
|
- (NSAttributedString *)createAttributedString |
|
{ |
|
[self addTextStoragesWithAtrributedString:_attString]; |
|
if (_attString == nil) { |
|
_attString = [[NSMutableAttributedString alloc] init]; |
|
} |
|
return [_attString copy]; |
|
} |
|
|
|
#pragma mark - setter |
|
- (void)setupProperty |
|
{ |
|
_font = [UIFont systemFontOfSize:15]; |
|
_characterSpacing = 1; |
|
_linesSpacing = 2; |
|
_paragraphSpacing = 0; |
|
#if __IPHONE_OS_VERSION_MAX_ALLOWED <= 9000 |
|
_textAlignment = kCTLeftTextAlignment; |
|
#else |
|
_textAlignment = kCTTextAlignmentLeft; |
|
#endif |
|
_lineBreakMode = kCTLineBreakByCharWrapping; |
|
_textColor = kTextColor; |
|
_linkColor = kLinkColor; |
|
_replaceStringNum = 0; |
|
} |
|
|
|
- (void)resetAllAttributed |
|
{ |
|
[self resetRectDictionary]; |
|
_textStorageArray = nil; |
|
_textStorages = nil; |
|
_replaceStringNum = 0; |
|
} |
|
|
|
- (void)resetRectDictionary |
|
{ |
|
_drawRectDictionary = nil; |
|
_linkRectDictionary = nil; |
|
_runRectDictionary = nil; |
|
} |
|
|
|
- (void)resetFrameRef |
|
{ |
|
if (_frameRef) { |
|
CFRelease(_frameRef); |
|
_frameRef = nil; |
|
} |
|
_textHeight = 0; |
|
} |
|
|
|
- (void)setText:(NSString *)text |
|
{ |
|
_attString = [self createTextAttibuteStringWithText:text]; |
|
[self resetAllAttributed]; |
|
[self resetFrameRef]; |
|
} |
|
|
|
- (void)setAttributedText:(NSAttributedString *)attributedText |
|
{ |
|
if (attributedText == nil) { |
|
_attString = [[NSMutableAttributedString alloc] init]; |
|
}else if ([attributedText isKindOfClass:[NSMutableAttributedString class]]) { |
|
_attString = (NSMutableAttributedString *)attributedText; |
|
}else { |
|
_attString = [[NSMutableAttributedString alloc] initWithAttributedString:attributedText]; |
|
} |
|
[self resetAllAttributed]; |
|
[self resetFrameRef]; |
|
} |
|
|
|
- (void)setTextColor:(UIColor *)textColor |
|
{ |
|
if (textColor && _textColor != textColor){ |
|
_textColor = textColor; |
|
|
|
[_attString addAttributeTextColor:textColor]; |
|
[self resetFrameRef]; |
|
} |
|
} |
|
|
|
- (void)setFont:(UIFont *)font |
|
{ |
|
if (font && _font != font){ |
|
_font = font; |
|
|
|
[_attString addAttributeFont:font]; |
|
[self resetFrameRef]; |
|
} |
|
} |
|
|
|
- (void)setStrokeWidth:(unichar)strokeWidth |
|
{ |
|
if (_strokeWidth != strokeWidth) { |
|
_strokeWidth = strokeWidth; |
|
[_attString addAttributeStrokeWidth:strokeWidth strokeColor:_strokeColor]; |
|
[self resetFrameRef]; |
|
} |
|
} |
|
|
|
- (void)setStrokeColor:(UIColor *)strokeColor |
|
{ |
|
if (strokeColor && _strokeColor != strokeColor) { |
|
_strokeColor = strokeColor; |
|
[_attString addAttributeStrokeWidth:_strokeWidth strokeColor:strokeColor]; |
|
[self resetFrameRef]; |
|
} |
|
} |
|
|
|
- (void)setCharacterSpacing:(unichar)characterSpacing |
|
{ |
|
if (_characterSpacing != characterSpacing) { |
|
_characterSpacing = characterSpacing; |
|
|
|
[_attString addAttributeCharacterSpacing:characterSpacing]; |
|
[self resetFrameRef]; |
|
} |
|
} |
|
|
|
- (void)setLinesSpacing:(CGFloat)linesSpacing |
|
{ |
|
if (_linesSpacing != linesSpacing) { |
|
_linesSpacing = linesSpacing; |
|
|
|
[self addAttributeAlignmentStyle:_textAlignment lineSpaceStyle:linesSpacing paragraphSpaceStyle:_paragraphSpacing lineBreakStyle:_lineBreakMode]; |
|
[self resetFrameRef]; |
|
} |
|
} |
|
|
|
- (void)setParagraphSpacing:(CGFloat)paragraphSpacing |
|
{ |
|
if (_paragraphSpacing != paragraphSpacing) { |
|
_paragraphSpacing = paragraphSpacing; |
|
[self addAttributeAlignmentStyle:_textAlignment lineSpaceStyle:_linesSpacing paragraphSpaceStyle:_paragraphSpacing lineBreakStyle:_lineBreakMode]; |
|
[self resetFrameRef]; |
|
} |
|
} |
|
|
|
- (void)setTextAlignment:(CTTextAlignment)textAlignment |
|
{ |
|
if (_textAlignment != textAlignment) { |
|
_textAlignment = textAlignment; |
|
|
|
[self addAttributeAlignmentStyle:textAlignment lineSpaceStyle:_linesSpacing paragraphSpaceStyle:_paragraphSpacing lineBreakStyle:_lineBreakMode]; |
|
[self resetFrameRef]; |
|
} |
|
} |
|
|
|
- (void)setLineBreakMode:(CTLineBreakMode)lineBreakMode |
|
{ |
|
if (_lineBreakMode != lineBreakMode) { |
|
_lineBreakMode = lineBreakMode; |
|
|
|
[self addAttributeAlignmentStyle:_textAlignment lineSpaceStyle:_linesSpacing paragraphSpaceStyle:_paragraphSpacing lineBreakStyle:_lineBreakMode]; |
|
[self resetFrameRef]; |
|
|
|
} |
|
} |
|
|
|
#pragma mark - create text attibuteString |
|
- (NSMutableAttributedString *)createTextAttibuteStringWithText:(NSString *)text |
|
{ |
|
if (text.length <= 0) { |
|
return [[NSMutableAttributedString alloc] init]; |
|
} |
|
// 创建属性文本 |
|
NSMutableAttributedString *attString = [[NSMutableAttributedString alloc] initWithString:text]; |
|
|
|
// 添加文本颜色 字体属性 |
|
[self addTextColorAndFontWithAtrributedString:attString]; |
|
|
|
// 添加文本段落样式 |
|
[self addTextParaphStyleWithAtrributedString:attString]; |
|
|
|
return attString; |
|
} |
|
|
|
// 添加文本颜色 字体属性 |
|
- (void)addTextColorAndFontWithAtrributedString:(NSMutableAttributedString *)attString |
|
{ |
|
// 添加文本字体 |
|
[attString addAttributeFont:_font]; |
|
|
|
// 添加文本颜色 |
|
[attString addAttributeTextColor:_textColor]; |
|
|
|
// 添加空心字体 |
|
if (_strokeWidth > 0) { |
|
[attString addAttributeStrokeWidth:_strokeWidth strokeColor:_strokeColor]; |
|
} |
|
|
|
} |
|
|
|
// 添加文本段落样式 |
|
- (void)addTextParaphStyleWithAtrributedString:(NSMutableAttributedString *)attString |
|
{ |
|
// 字体间距 |
|
if (_characterSpacing) |
|
{ |
|
[attString addAttributeCharacterSpacing:_characterSpacing]; |
|
} |
|
|
|
// 添加文本段落样式 |
|
[self addAttributeAlignmentStyle:_textAlignment lineSpaceStyle:_linesSpacing paragraphSpaceStyle:_paragraphSpacing lineBreakStyle:_lineBreakMode]; |
|
} |
|
|
|
- (void)addAttributeAlignmentStyle:(CTTextAlignment)textAlignment |
|
lineSpaceStyle:(CGFloat)linesSpacing |
|
paragraphSpaceStyle:(CGFloat)paragraphSpacing |
|
lineBreakStyle:(CTLineBreakMode)lineBreakMode |
|
{ |
|
if (lineBreakMode == kCTLineBreakByTruncatingTail) |
|
{ |
|
lineBreakMode = _numberOfLines == 1 ? kCTLineBreakByCharWrapping : kCTLineBreakByWordWrapping; |
|
} |
|
[_attString addAttributeAlignmentStyle:_textAlignment lineSpaceStyle:_linesSpacing paragraphSpaceStyle:_paragraphSpacing lineBreakStyle:lineBreakMode]; |
|
} |
|
|
|
#pragma mark - add text storage atrributed |
|
- (void)addTextStoragesWithAtrributedString:(NSMutableAttributedString *)attString |
|
{ |
|
if (attString && _textStorageArray.count > 0) { |
|
|
|
// 排序range |
|
[self sortTextStorageArray:_textStorageArray]; |
|
|
|
for (id<TYTextStorageProtocol> textStorage in _textStorageArray) { |
|
|
|
// 修正图片替换字符来的误差 |
|
if ([textStorage conformsToProtocol:@protocol(TYDrawStorageProtocol) ]) { |
|
continue; |
|
} |
|
|
|
if ([textStorage conformsToProtocol:@protocol(TYLinkStorageProtocol)]) { |
|
if (!((id<TYLinkStorageProtocol>)textStorage).textColor) { |
|
((id<TYLinkStorageProtocol>)textStorage).textColor = _linkColor; |
|
} |
|
} |
|
|
|
// 验证范围 |
|
if (NSMaxRange(textStorage.range) <= attString.length) { |
|
[textStorage addTextStorageWithAttributedString:attString]; |
|
} |
|
|
|
} |
|
|
|
for (id<TYTextStorageProtocol> textStorage in _textStorageArray) { |
|
textStorage.realRange = NSMakeRange(textStorage.range.location-_replaceStringNum, textStorage.range.length); |
|
if ([textStorage conformsToProtocol:@protocol(TYDrawStorageProtocol)]) { |
|
id<TYDrawStorageProtocol> drawStorage = (id<TYDrawStorageProtocol>)textStorage; |
|
NSInteger currentLenght = attString.length; |
|
[drawStorage setTextfontAscent:_font.ascender descent:_font.descender]; |
|
[drawStorage currentReplacedStringNum:_replaceStringNum]; |
|
[drawStorage addTextStorageWithAttributedString:attString]; |
|
_replaceStringNum += currentLenght - attString.length; |
|
} |
|
} |
|
_textStorages = [_textStorageArray copy]; |
|
[_textStorageArray removeAllObjects]; |
|
} |
|
} |
|
|
|
- (void)sortTextStorageArray:(NSMutableArray *)textStorageArray |
|
{ |
|
[textStorageArray sortUsingComparator:^NSComparisonResult(id<TYTextStorageProtocol> obj1, id<TYTextStorageProtocol> obj2) { |
|
if (obj1.range.location < obj2.range.location) { |
|
return NSOrderedAscending; |
|
} else if (obj1.range.location > obj2.range.location){ |
|
return NSOrderedDescending; |
|
}else { |
|
return obj1.range.length > obj2.range.length ? NSOrderedAscending:NSOrderedDescending; |
|
} |
|
}]; |
|
} |
|
|
|
- (void)saveTextStorageRectWithFrame:(CTFrameRef)frame |
|
{ |
|
if (!frame) { |
|
return; |
|
} |
|
// 获取每行 |
|
CFArrayRef lines = CTFrameGetLines(frame); |
|
CGPoint lineOrigins[CFArrayGetCount(lines)]; |
|
CTFrameGetLineOrigins(frame, CFRangeMake(0, 0), lineOrigins); |
|
CGFloat viewWidth = _textWidth; |
|
|
|
NSInteger numberOfLines = _numberOfLines > 0 ? MIN(_numberOfLines, CFArrayGetCount(lines)) : CFArrayGetCount(lines); |
|
|
|
NSMutableDictionary *runRectDictionary = [NSMutableDictionary dictionary]; |
|
NSMutableDictionary *linkRectDictionary = [NSMutableDictionary dictionary]; |
|
NSMutableDictionary *drawRectDictionary = [NSMutableDictionary dictionary]; |
|
// 获取每行有多少run |
|
for (int i = 0; i < numberOfLines; i++) { |
|
CTLineRef line = CFArrayGetValueAtIndex(lines, i); |
|
CGFloat lineAscent; |
|
CGFloat lineDescent; |
|
CGFloat lineLeading; |
|
CTLineGetTypographicBounds(line, &lineAscent, &lineDescent, &lineLeading); |
|
|
|
CFArrayRef runs = CTLineGetGlyphRuns(line); |
|
// 获得每行的run |
|
for (int j = 0; j < CFArrayGetCount(runs); j++) { |
|
CGFloat runAscent; |
|
CGFloat runDescent; |
|
CGPoint lineOrigin = lineOrigins[i]; |
|
CTRunRef run = CFArrayGetValueAtIndex(runs, j); |
|
// run的属性字典 |
|
NSDictionary* attributes = (NSDictionary*)CTRunGetAttributes(run); |
|
id<TYTextStorageProtocol> textStorage = [attributes objectForKey:kTYTextRunAttributedName]; |
|
|
|
if (textStorage) { |
|
CGFloat runWidth = CTRunGetTypographicBounds(run, CFRangeMake(0,0), &runAscent, &runDescent, NULL); |
|
|
|
if (viewWidth > 0 && runWidth > viewWidth) { |
|
runWidth = viewWidth; |
|
} |
|
CGRect runRect = CGRectMake(lineOrigin.x + CTLineGetOffsetForStringIndex(line, CTRunGetStringRange(run).location, NULL), lineOrigin.y - runDescent, runWidth, runAscent + runDescent); |
|
|
|
if ([textStorage conformsToProtocol:@protocol(TYDrawStorageProtocol)]) { |
|
[drawRectDictionary setObject:textStorage forKey:[NSValue valueWithCGRect:runRect]]; |
|
} else if ([textStorage conformsToProtocol:@protocol(TYLinkStorageProtocol)]) { |
|
[linkRectDictionary setObject:textStorage forKey:[NSValue valueWithCGRect:runRect]]; |
|
} |
|
|
|
[runRectDictionary setObject:textStorage forKey:[NSValue valueWithCGRect:runRect]]; |
|
} |
|
} |
|
} |
|
|
|
if (drawRectDictionary.count > 0) { |
|
_drawRectDictionary = [drawRectDictionary copy]; |
|
}else { |
|
_drawRectDictionary = nil; |
|
} |
|
|
|
if (runRectDictionary.count > 0) { |
|
// 添加响应点击rect |
|
[self addRunRectDictionary:[runRectDictionary copy]]; |
|
} |
|
|
|
if (linkRectDictionary.count > 0) { |
|
_linkRectDictionary = [linkRectDictionary copy]; |
|
}else { |
|
_linkRectDictionary = nil; |
|
} |
|
} |
|
|
|
// 添加响应点击rect |
|
- (void)addRunRectDictionary:(NSDictionary *)runRectDictionary |
|
{ |
|
if (runRectDictionary.count < _runRectDictionary.count) { |
|
NSMutableArray *drawStorageArray = [[_runRectDictionary allValues]mutableCopy]; |
|
// 剔除已经画出来的 |
|
[drawStorageArray removeObjectsInArray:[runRectDictionary allValues]]; |
|
|
|
// 遍历不会画出来的 |
|
for (id<TYTextStorageProtocol>drawStorage in drawStorageArray) { |
|
if ([drawStorage conformsToProtocol:@protocol(TYViewStorageProtocol)]) { |
|
[(id<TYViewStorageProtocol>)drawStorage didNotDrawRun]; |
|
} |
|
} |
|
} |
|
_runRectDictionary = runRectDictionary; |
|
} |
|
|
|
- (CGSize)getSuggestedSizeWithFramesetter:(CTFramesetterRef)framesetter width:(CGFloat)width |
|
{ |
|
if (_attString == nil || width <= 0) { |
|
return CGSizeZero; |
|
} |
|
|
|
if (_textHeight > 0) { |
|
return CGSizeMake(_textWidth > 0 ? _textWidth : width, _textHeight); |
|
} |
|
|
|
// 是否需要更新frame |
|
if (framesetter == nil) { |
|
|
|
framesetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)[self createAttributedString]); |
|
}else { |
|
CFRetain(framesetter); |
|
} |
|
|
|
// 获得建议的size |
|
CGSize suggestedSize = CTFramesetterSuggestFrameSizeForAttributedStringWithConstraints(framesetter, _attString, CGSizeMake(width,MAXFLOAT), _numberOfLines); |
|
|
|
CFRelease(framesetter); |
|
|
|
return CGSizeMake(_isWidthToFit ? suggestedSize.width : width, suggestedSize.height+1); |
|
} |
|
- (CGFloat)getHeightWithFramesetter:(CTFramesetterRef)framesetter width:(CGFloat)width |
|
{ |
|
return [self getSuggestedSizeWithFramesetter:framesetter width:width].height; |
|
} |
|
|
|
- (CTFrameRef)createFrameRefWithFramesetter:(CTFramesetterRef)framesetter textSize:(CGSize)textSize |
|
{ |
|
// 这里你需要创建一个用于绘制文本的路径区域,通过 self.bounds 使用整个视图矩形区域创建 CGPath 引用。 |
|
CGMutablePathRef path = CGPathCreateMutable(); |
|
CGFloat textHeight = [self getHeightWithFramesetter:framesetter width:textSize.width]; |
|
CGPathAddRect(path, NULL, CGRectMake(0, 0, textSize.width, MAX(textHeight, textSize.height))); |
|
|
|
CTFrameRef frameRef = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, [_attString length]), path, NULL); |
|
CFRelease(path); |
|
return frameRef; |
|
} |
|
|
|
- (instancetype)createTextContainerWithTextWidth:(CGFloat)textWidth |
|
{ |
|
return [self createTextContainerWithContentSize:CGSizeMake(textWidth, 0)]; |
|
} |
|
|
|
- (instancetype)createTextContainerWithContentSize:(CGSize)contentSize |
|
{ |
|
if (_frameRef) { |
|
return self; |
|
} |
|
NSAttributedString *attStr = [self createAttributedString]; |
|
// 创建CTFramesetter |
|
CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)attStr); |
|
|
|
// 获得建议的size |
|
CGSize size = [self getSuggestedSizeWithFramesetter:framesetter width:contentSize.width]; |
|
_textWidth = size.width; |
|
_textHeight = size.height; |
|
|
|
// 创建CTFrameRef |
|
_frameRef = [self createFrameRefWithFramesetter:framesetter textSize:CGSizeMake(_textWidth, contentSize.height > 0 ? contentSize.height : _textHeight)]; |
|
|
|
// 释放内存 |
|
CFRelease(framesetter); |
|
|
|
// 保存run rect |
|
[self saveTextStorageRectWithFrame:_frameRef]; |
|
|
|
return self; |
|
} |
|
|
|
#pragma mark - enumerate runRect |
|
|
|
- (BOOL)existRunRectDictionary |
|
{ |
|
return _runRectDictionary.count != 0; |
|
} |
|
|
|
- (BOOL)existLinkRectDictionary |
|
{ |
|
return _linkRectDictionary.count != 0; |
|
} |
|
|
|
- (BOOL)existDrawRectDictionary |
|
{ |
|
return _drawRectDictionary.count != 0; |
|
} |
|
|
|
- (void)enumerateDrawRectDictionaryUsingBlock:(void (^)(id<TYDrawStorageProtocol> drawStorage, CGRect rect))block |
|
{ |
|
[_drawRectDictionary enumerateKeysAndObjectsUsingBlock:^(NSValue *rectValue, id<TYDrawStorageProtocol> drawStorage, BOOL * stop) { |
|
if (block) { |
|
block(drawStorage,[rectValue CGRectValue]); |
|
} |
|
}]; |
|
} |
|
|
|
- (BOOL)enumerateRunRectContainPoint:(CGPoint)point viewHeight:(CGFloat)viewHeight successBlock:(void (^)(id<TYTextStorageProtocol> textStorage))successBlock |
|
{ |
|
return [self enumerateRunRect:_runRectDictionary ContainPoint:point viewHeight:viewHeight successBlock:successBlock]; |
|
} |
|
|
|
- (BOOL)enumerateLinkRectContainPoint:(CGPoint)point viewHeight:(CGFloat)viewHeight successBlock:(void (^)(id<TYLinkStorageProtocol> textStorage))successBlock |
|
{ |
|
return [self enumerateRunRect:_linkRectDictionary ContainPoint:point viewHeight:viewHeight successBlock:successBlock]; |
|
} |
|
|
|
- (BOOL)enumerateRunRect:(NSDictionary *)runRectDic ContainPoint:(CGPoint)point viewHeight:(CGFloat)viewHeight successBlock:(void (^)(id textStorage))successBlock |
|
{ |
|
if (runRectDic.count == 0) { |
|
return NO; |
|
} |
|
// CoreText context coordinates are the opposite to UIKit so we flip the bounds |
|
CGAffineTransform transform = CGAffineTransformScale(CGAffineTransformMakeTranslation(0, viewHeight), 1.f, -1.f); |
|
|
|
__block BOOL find = NO; |
|
// 遍历run位置字典 |
|
[runRectDic enumerateKeysAndObjectsUsingBlock:^(NSValue *keyRectValue, id<TYTextStorageProtocol> textStorage, BOOL *stop) { |
|
|
|
CGRect imgRect = [keyRectValue CGRectValue]; |
|
CGRect rect = CGRectApplyAffineTransform(imgRect, transform); |
|
|
|
if ([textStorage conformsToProtocol:@protocol(TYDrawStorageProtocol) ]) { |
|
rect = UIEdgeInsetsInsetRect(rect,((id<TYDrawStorageProtocol>)textStorage).margin); |
|
} |
|
|
|
// point 是否在rect里 |
|
if(CGRectContainsPoint(rect, point)){ |
|
find = YES; |
|
*stop = YES; |
|
if (successBlock) { |
|
successBlock(textStorage); |
|
} |
|
} |
|
}]; |
|
return find; |
|
} |
|
|
|
- (void)dealloc{ |
|
[self resetFrameRef]; |
|
} |
|
|
|
@end |
|
|
|
#pragma mark - add textStorage |
|
@implementation TYTextContainer (Add) |
|
|
|
- (void)addTextStorage:(id<TYTextStorageProtocol>)textStorage |
|
{ |
|
if (textStorage) { |
|
[self.textStorageArray addObject:textStorage]; |
|
[self resetFrameRef]; |
|
} |
|
} |
|
|
|
- (void)addTextStorageArray:(NSArray *)textStorageArray |
|
{ |
|
if (textStorageArray) { |
|
for (id<TYTextStorageProtocol> textStorage in textStorageArray) { |
|
if ([textStorage conformsToProtocol:@protocol(TYTextStorageProtocol)]) { |
|
[self addTextStorage:textStorage]; |
|
} |
|
} |
|
} |
|
} |
|
@end |
|
|
|
#pragma mark - append textStorage |
|
@implementation TYTextContainer (Append) |
|
|
|
- (void)appendText:(NSString *)text |
|
{ |
|
NSAttributedString *attributedText = [self createTextAttibuteStringWithText:text]; |
|
[self appendTextAttributedString:attributedText]; |
|
[self resetFrameRef]; |
|
} |
|
|
|
- (void)appendTextAttributedString:(NSAttributedString *)attributedText |
|
{ |
|
if (attributedText == nil) { |
|
return; |
|
} |
|
if (_attString == nil) { |
|
_attString = [[NSMutableAttributedString alloc] init]; |
|
} |
|
|
|
if ([attributedText isKindOfClass:[NSMutableAttributedString class]]) { |
|
[self addTextParaphStyleWithAtrributedString:(NSMutableAttributedString *)attributedText]; |
|
} |
|
|
|
[_attString appendAttributedString:attributedText]; |
|
[self resetFrameRef]; |
|
} |
|
|
|
- (void)appendTextStorage:(id<TYAppendTextStorageProtocol>)textStorage |
|
{ |
|
if (textStorage) { |
|
if ([textStorage conformsToProtocol:@protocol(TYDrawStorageProtocol)]) { |
|
[(id<TYDrawStorageProtocol>)textStorage setTextfontAscent:_font.ascender descent:_font.descender]; |
|
} else if ([textStorage conformsToProtocol:@protocol(TYLinkStorageProtocol)]) { |
|
if (!((id<TYLinkStorageProtocol>)textStorage).textColor) { |
|
((id<TYLinkStorageProtocol>)textStorage).textColor = _linkColor; |
|
} |
|
} |
|
|
|
NSAttributedString *attAppendString = [textStorage appendTextStorageAttributedString]; |
|
textStorage.realRange = NSMakeRange(_attString.length, attAppendString.length); |
|
[self appendTextAttributedString:attAppendString]; |
|
[self resetFrameRef]; |
|
} |
|
} |
|
|
|
- (void)appendTextStorageArray:(NSArray *)textStorageArray |
|
{ |
|
if (textStorageArray) { |
|
for (id<TYAppendTextStorageProtocol> textStorage in textStorageArray) { |
|
if ([textStorage conformsToProtocol:@protocol(TYAppendTextStorageProtocol)]) { |
|
[self appendTextStorage:textStorage]; |
|
} |
|
} |
|
} |
|
} |
|
|
|
@end
|
|
|