小说绘上架版本

This commit is contained in:
xtfei2011
2021-02-07 11:24:08 +08:00
commit ee5c1c8b12
1762 changed files with 115892 additions and 0 deletions
@@ -0,0 +1,87 @@
//
// NSMutableAttributedString+TY.h
// TYAttributedLabelDemo
//
// Created by tanyang on 15/4/8.
// Copyright (c) 2015年 tanyang. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import <CoreText/CoreText.h>
@interface NSMutableAttributedString (TY)
/**
* 添加文本颜色属性
*
* @param color 文本颜色
*/
- (void)addAttributeTextColor:(UIColor*)color;
- (void)addAttributeTextColor:(UIColor*)color range:(NSRange)range;
/**
* 添加文本字体属性
*
* @param font 字体
*/
- (void)addAttributeFont:(UIFont *)font;
- (void)addAttributeFont:(UIFont *)font range:(NSRange)range;
/**
* 添加文本字符间隔
*
* @param characterSpacing 字符间隔
*/
- (void)addAttributeCharacterSpacing:(unichar)characterSpacing;
- (void)addAttributeCharacterSpacing:(unichar)characterSpacing range:(NSRange)range;
/**
* 添加下划线样式
*
* @param style 下划线 (单下划线 双 无)
* @param modifier 下划线样式 (点 线)
*/
- (void)addAttributeUnderlineStyle:(CTUnderlineStyle)style
modifier:(CTUnderlineStyleModifiers)modifier;
- (void)addAttributeUnderlineStyle:(CTUnderlineStyle)style
modifier:(CTUnderlineStyleModifiers)modifier
range:(NSRange)range;
/**
* 添加空心字
*
* @param strokeWidth 空心字边框宽
* @param strokeColor 空心字边框颜色
*/
- (void)addAttributeStrokeWidth:(unichar)strokeWidth
strokeColor:(UIColor *)strokeColor;
- (void)addAttributeStrokeWidth:(unichar)strokeWidth
strokeColor:(UIColor *)strokeColor
range:(NSRange)range;
/**
* 添加文本段落样式
*
* @param textAlignment 文本对齐样式
* @param linesSpacing 文本行间距
* @param paragraphSpacing 段落间距
* @param lineBreakMode 文本换行样式
*/
- (void)addAttributeAlignmentStyle:(CTTextAlignment)textAlignment
lineSpaceStyle:(CGFloat)linesSpacing
paragraphSpaceStyle:(CGFloat)paragraphSpacing
lineBreakStyle:(CTLineBreakMode)lineBreakMode;
- (void)addAttributeAlignmentStyle:(CTTextAlignment)textAlignment
lineSpaceStyle:(CGFloat)linesSpacing
paragraphSpaceStyle:(CGFloat)paragraphSpacing
lineBreakStyle:(CTLineBreakMode)lineBreakMode
range:(NSRange)range;
@end
@@ -0,0 +1,168 @@
//
// NSMutableAttributedString+TY.m
// TYAttributedLabelDemo
//
// Created by tanyang on 15/4/8.
// Copyright (c) 2015年 tanyang. All rights reserved.
//
#import "NSMutableAttributedString+TY.h"
@implementation NSMutableAttributedString (TY)
#pragma mark - 文本颜色属性
- (void)addAttributeTextColor:(UIColor*)color
{
[self addAttributeTextColor:color range:NSMakeRange(0, [self length])];
}
- (void)addAttributeTextColor:(UIColor*)color range:(NSRange)range
{
if (color.CGColor)
{
[self removeAttribute:(NSString *)kCTForegroundColorAttributeName range:range];
[self addAttribute:(NSString *)kCTForegroundColorAttributeName
value:(id)color.CGColor
range:range];
}
}
#pragma mark - 文本字体属性
- (void)addAttributeFont:(UIFont *)font
{
[self addAttributeFont:font range:NSMakeRange(0, [self length])];
}
- (void)addAttributeFont:(UIFont *)font range:(NSRange)range
{
if (font)
{
[self removeAttribute:(NSString*)kCTFontAttributeName range:range];
CTFontRef fontRef = CTFontCreateWithName((CFStringRef)@"SFUI-Regular", font.pointSize, nil);
if (nil != fontRef)
{
[self addAttribute:(NSString *)kCTFontAttributeName value:(__bridge id)fontRef range:range];
CFRelease(fontRef);
}
}
}
#pragma mark - 文本字符间隔属性
- (void)addAttributeCharacterSpacing:(unichar)characterSpacing
{
[self addAttributeCharacterSpacing:characterSpacing range:NSMakeRange(0, self.length)];
}
- (void)addAttributeCharacterSpacing:(unichar)characterSpacing range:(NSRange)range
{
[self removeAttribute:(id)kCTKernAttributeName range:range];
CFNumberRef num = CFNumberCreate(kCFAllocatorDefault,kCFNumberSInt16Type,&characterSpacing);
[self addAttribute:(id)kCTKernAttributeName value:(__bridge id)num range:range];
CFRelease(num);
}
#pragma mark - 文本下划线属性
- (void)addAttributeUnderlineStyle:(CTUnderlineStyle)style
modifier:(CTUnderlineStyleModifiers)modifier
{
[self addAttributeUnderlineStyle:style
modifier:modifier
range:NSMakeRange(0, self.length)];
}
- (void)addAttributeUnderlineStyle:(CTUnderlineStyle)style
modifier:(CTUnderlineStyleModifiers)modifier
range:(NSRange)range
{
[self removeAttribute:(NSString *)kCTUnderlineColorAttributeName range:range];
if (style != kCTUnderlineStyleNone) {
[self addAttribute:(NSString *)kCTUnderlineStyleAttributeName
value:[NSNumber numberWithInt:(style|modifier)]
range:range];
}
}
#pragma mark - 文本空心字及颜色
- (void)addAttributeStrokeWidth:(unichar)strokeWidth
strokeColor:(UIColor *)strokeColor
{
[self addAttributeStrokeWidth:strokeWidth strokeColor:strokeColor range:NSMakeRange(0, self.length)];
}
- (void)addAttributeStrokeWidth:(unichar)strokeWidth
strokeColor:(UIColor *)strokeColor
range:(NSRange)range
{
[self removeAttribute:(id)kCTStrokeWidthAttributeName range:range];
if (strokeWidth > 0) {
CFNumberRef num = CFNumberCreate(kCFAllocatorDefault,kCFNumberSInt16Type,&strokeWidth);
[self addAttribute:(id)kCTStrokeWidthAttributeName value:(__bridge id)num range:range];
CFRelease(num);
}
[self removeAttribute:(id)kCTStrokeColorAttributeName range:range];
if (strokeColor) {
[self addAttribute:(id)kCTStrokeColorAttributeName value:(id)strokeColor.CGColor range:range];
}
}
#pragma mark - 文本段落样式属性
- (void)addAttributeAlignmentStyle:(CTTextAlignment)textAlignment
lineSpaceStyle:(CGFloat)linesSpacing
paragraphSpaceStyle:(CGFloat)paragraphSpacing
lineBreakStyle:(CTLineBreakMode)lineBreakMode
{
[self addAttributeAlignmentStyle:textAlignment lineSpaceStyle:linesSpacing paragraphSpaceStyle:paragraphSpacing lineBreakStyle:lineBreakMode range:NSMakeRange(0, self.length)];
}
- (void)addAttributeAlignmentStyle:(CTTextAlignment)textAlignment
lineSpaceStyle:(CGFloat)linesSpacing
paragraphSpaceStyle:(CGFloat)paragraphSpacing
lineBreakStyle:(CTLineBreakMode)lineBreakMode
range:(NSRange)range
{
[self removeAttribute:(id)kCTParagraphStyleAttributeName range:range];
// 创建文本对齐方式
CTParagraphStyleSetting alignmentStyle;
alignmentStyle.spec = kCTParagraphStyleSpecifierAlignment;//指定为对齐属性
alignmentStyle.valueSize = sizeof(textAlignment);
alignmentStyle.value = &textAlignment;
// 创建文本行间距
CTParagraphStyleSetting lineSpaceStyle;
lineSpaceStyle.spec = kCTParagraphStyleSpecifierLineSpacingAdjustment;
lineSpaceStyle.valueSize = sizeof(linesSpacing);
lineSpaceStyle.value = &linesSpacing;
//段落间距
CTParagraphStyleSetting paragraphSpaceStyle;
paragraphSpaceStyle.spec = kCTParagraphStyleSpecifierParagraphSpacing;
paragraphSpaceStyle.value = &paragraphSpacing;
paragraphSpaceStyle.valueSize = sizeof(paragraphSpacing);
//换行模式
CTParagraphStyleSetting lineBreakStyle;
lineBreakStyle.spec = kCTParagraphStyleSpecifierLineBreakMode;
lineBreakStyle.value = &lineBreakMode;
lineBreakStyle.valueSize = sizeof(lineBreakMode);
// 创建样式数组
CTParagraphStyleSetting settings[] = {alignmentStyle ,lineSpaceStyle, paragraphSpaceStyle, lineBreakStyle};
CTParagraphStyleRef paragraphStyle = CTParagraphStyleCreate(settings, sizeof(settings) / sizeof(settings[0])); // 设置样式
// 设置段落属性
[self addAttribute:(id)kCTParagraphStyleAttributeName value:(id)CFBridgingRelease(paragraphStyle) range:range];
}
@end
@@ -0,0 +1,329 @@
//
// TYAttributedLabel.h
// TYAttributedLabelDemo
//
// Created by tanyang on 15/4/8.
// Copyright (c) 2015年 tanyang. All rights reserved.
// TYAttributedLabel v2.0 verson
#import <UIKit/UIKit.h>
#import "TYTextContainer.h"
typedef NS_ENUM(NSUInteger, TYVerticalAlignment) {
TYVerticalAlignmentTop,
TYVerticalAlignmentCenter,
TYVerticalAlignmentBottom,
};
@class TYAttributedLabel;
@protocol TYAttributedLabelDelegate <NSObject>
@optional
// 点击代理
- (void)attributedLabel:(TYAttributedLabel *)attributedLabel textStorageClicked:(id<TYTextStorageProtocol>)textStorage atPoint:(CGPoint)point;
// 长按代理 有多个状态 begin, changes, end 都会调用,所以需要判断状态
- (void)attributedLabel:(TYAttributedLabel *)attributedLabel textStorageLongPressed:(id<TYTextStorageProtocol>)textStorage onState:(UIGestureRecognizerState)state atPoint:(CGPoint)point;
// 长按非Container区域代理 有多个状态 begin, changes, end 都会调用,所以需要判断状态
- (void)attributedLabel:(TYAttributedLabel *)attributedLabel lableLongPressOnState:(UIGestureRecognizerState)state atPoint:(CGPoint)point;
@end
/**
* TYAttributedLabel 属性文本 支持图文混排显示,支持添加image和UIView,支持自定义排版
*/
@interface TYAttributedLabel : UIView
@property (nonatomic, assign) id<TYAttributedLabelDelegate> delegate;
@property (nonatomic, strong) NSString *text;
@property (nonatomic, strong) UIColor *textColor; // 文字颜色
@property (nonatomic, strong) UIFont *font; // 文字大小
@property (nonatomic,assign) NSInteger numberOfLines; //行数
@property (nonatomic, strong) UIColor *linkColor; //链接颜色
@property (nonatomic, strong) UIColor *highlightedLinkColor;//默认nil高亮链接颜色
@property (nonatomic, strong) UIColor *highlightedLinkBackgroundColor;//链接高亮背景颜色
@property (nonatomic, assign) CGFloat highlightedLinkBackgroundRadius; // 高亮背景圆角
@property (nonatomic, assign) unichar strokeWidth; // 空心字边框宽
@property (nonatomic, strong) UIColor *strokeColor; // 空心字边框颜色
@property (nonatomic, assign) unichar characterSpacing; // 字距
@property (nonatomic, assign) CGFloat linesSpacing; // 行距
@property (nonatomic, assign) CGFloat paragraphSpacing; // 段落间距
@property (nonatomic, assign) CTTextAlignment textAlignment; // 文本对齐方式 kCTTextAlignmentLeft
@property (nonatomic, assign) CTLineBreakMode lineBreakMode; // 换行模式 kCTLineBreakByCharWrapping
@property (nonatomic, assign) TYVerticalAlignment verticalAlignment; // 垂直对齐方式 默认是向上对齐
@property (nonatomic, strong) TYTextContainer *textContainer;
@property (nonatomic, assign) CGFloat preferredMaxLayoutWidth; // Autolayout
@property (nonatomic, assign) BOOL isWidthToFit; // 宽度自适应 默认NO
/**
* 获取普通文本内容
*/
- (NSString *)text;
/**
* 获取属性文本内容
*/
- (NSAttributedString *)attributedText;
/**
* 设置普通初始化文本内容
*
* @param text 普通文本内容
*/
- (void)setText:(NSString *)text;
/**
* 设置属性初始化文本内容
*
* @param attributedText 属性文本内容
*/
- (void)setAttributedText: (NSAttributedString *)attributedText;
/**
* 添加 textStorage (自定义显示内容)
*
* @param textStorage 自定义
*/
- (void)addTextStorage:(id<TYTextStorageProtocol>)textStorage;
/**
* 添加 textRun数组 (自定义显示内容)
*
*/
- (void)addTextStorageArray:(NSArray *)textStorageArray;
/**
* 调用可以自动计算frame大小(请确定label之前设置了宽度)
*/
- (void)sizeToFit;
/**
* 获取文本真正的高度
*/
- (int)getHeightWithWidth:(CGFloat)width;
/**
* 获取文本真正的size
*/
- (CGSize)getSizeWithWidth:(CGFloat)width;
/**
* 设置文本位置大小 (自动计算高度,根据宽度)
*/
- (void)setFrameWithOrign:(CGPoint)orign Width:(CGFloat)width;
@end
#pragma mark - 扩展追加内容 (Append)
// 追加内容 (添加在AttributedString最后)
@interface TYAttributedLabel (AppendAttributedString)
/**
* 追加(添加到最后) 普通文本
*
* @param text 普通文本
*/
- (void)appendText:(NSString *)text;
/**
* 追加(添加到最后) 属性文本
*
* @param attributedText 属性文本
*/
- (void)appendTextAttributedString: (NSAttributedString *)attributedText;
/**
* 追加(添加到最后) textStorage (自定义显示内容)
*
* @param textStorage 自定义Storage(自定义显示内容)
*/
- (void)appendTextStorage:(id<TYAppendTextStorageProtocol>)textStorage;
/**
* 追加(添加到最后) textStorage 数组
*
* @param textStorageArray 自定义run数组(需遵循协议TYAppendTextStorageProtocol,否则不会添加)
*/
- (void)appendTextStorageArray:(NSArray *)textStorageArray;
@end
#pragma mark - 扩展支持Link链接
@interface TYAttributedLabel (Link)
/**
* 添加 链接LinkTextStorage
*/
- (void)addLinkWithLinkData:(id)linkData range:(NSRange )range;
/**
* 添加 链接LinkTextStorage
*
* @param linkData 链接携带的数据
* @param linkColor 链接颜色
* @param range 范围
*/
- (void)addLinkWithLinkData:(id)linkData linkColor:(UIColor *)linkColor range:(NSRange )range;
- (void)addLinkWithLinkData:(id)linkData linkColor:(UIColor *)linkColor underLineStyle:(CTUnderlineStyle)underLineStyle range:(NSRange )range;
/**
* 追加 链接LinkTextStorage
*/
- (void)appendLinkWithText:(NSString *)linkText linkFont:(UIFont *)linkFont linkData:(id)linkData;
/**
* 追加 链接LinkTextStorage
*
* @param linkText 链接文本
* @param linkData 链接携带的数据
*/
- (void)appendLinkWithText:(NSString *)linkText linkFont:(UIFont *)linkFont linkColor:(UIColor *)linkColor linkData:(id)linkData;
- (void)appendLinkWithText:(NSString *)linkText linkFont:(UIFont *)linkFont linkColor:(UIColor *)linkColor underLineStyle:(CTUnderlineStyle)underLineStyle linkData:(id)linkData;
@end
#pragma mark - 扩展支持UIImage
@interface TYAttributedLabel (UIImage)
#pragma mark - addImageStorage
/**
* 添加 imageStorage image数据
*/
- (void)addImage:(UIImage *)image range:(NSRange)range;
/**
* 添加 imageStorage image数据
*/
- (void)addImage:(UIImage *)image range:(NSRange)range size:(CGSize)size;
/**
* 添加 imageStorage image数据
*
* @param image image
* @param range 所在文本位置
* @param size 图片大小
* @param alignment 图片对齐方式
*/
- (void)addImage:(UIImage *)image range:(NSRange)range
size:(CGSize)size
alignment: (TYDrawAlignment)alignment;
/**
* 添加 imageStorage image数据
*/
- (void)addImageWithName:(NSString *)imageName range:(NSRange)range;
/**
* 添加 imageStorage image数据
*/
- (void)addImageWithName:(NSString *)imageName
range:(NSRange)range
size:(CGSize)size;
/**
* 添加 imageStorage image数据
*
* @param imageName image名
* @param range 所在文本位置
* @param size 图片大小
* @param alignment 图片对齐方式
*/
- (void)addImageWithName:(NSString *)imageName
range:(NSRange)range
size:(CGSize)size
alignment:(TYDrawAlignment)alignment;
#pragma mark - appendImageStorage
/**
* 追加 imageStorage image数据
*/
- (void)appendImage:(UIImage *)image;
/**
* 追加 imageStorage image数据
*/
- (void)appendImage:(UIImage *)image
size:(CGSize)size;
/**
* 追加 imageStorage image数据
*
* @param image image
* @param size 图片大小
* @param alignment 图片对齐
*/
- (void)appendImage:(UIImage *)image
size:(CGSize)size
alignment:(TYDrawAlignment)alignment;
/**
* 追加 imageStorage image数据
*/
- (void)appendImageWithName:(NSString *)imageName;
/**
* 追加 imageStorage image数据
*/
- (void)appendImageWithName:(NSString *)imageName size:(CGSize)size;
/**
* 追加 imageStorage image数据
*
* @param imageName imageName
* @param size 图片大小
* @param alignment 图片对齐
*/
- (void)appendImageWithName:(NSString *)imageName
size:(CGSize)size
alignment:(TYDrawAlignment)alignment;
@end
#pragma mark - 扩展支持UIView
@interface TYAttributedLabel (UIView)
/**
* 添加 viewStorage (添加 UI控件 需要设置frame)
*/
- (void)addView:(UIView *)view range:(NSRange)range;
/**
* 添加 viewStorage (添加 UI控件 需要设置frame)
*
* @param view UIView (UI控件)
* @param range 所在文本位置
* @param alignment view对齐方式
*/
- (void)addView:(UIView *)view
range:(NSRange)range
alignment:(TYDrawAlignment)alignment;
/**
* 追加 viewStorage (添加 UI控件 需要设置frame)
*/
- (void)appendView:(UIView *)view;
/**
* 追加 viewStorage (添加 UI控件 需要设置frame)
*
* @param view UIView (UI控件)
* @param alignment view对齐
*/
- (void)appendView:(UIView *)view alignment:(TYDrawAlignment)alignment;
@end
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,40 @@
//
// TYDrawStorage.h
// TYAttributedLabelDemo
//
// Created by tanyang on 15/4/8.
// Copyright (c) 2015年 tanyang. All rights reserved.
//
#import "TYTextStorageProtocol.h"
@interface TYDrawStorage : NSObject<TYDrawStorageProtocol>
@property (nonatomic, assign) NSInteger tag; // 标识
@property (nonatomic, assign) NSRange range; // 文本范围
@property (nonatomic, assign) NSRange realRange;
@property (nonatomic, assign) UIEdgeInsets margin; // 图片四周间距
@property (nonatomic, assign) CGSize size; // 绘画物大小
@property (nonatomic, assign) TYDrawAlignment drawAlignment; // 对齐方式
/**
* 获取绘画区域上行高度(默认实现)
*/
- (CGFloat)getDrawRunAscentHeight;
/**
* 获取绘画区域下行高度 默认实现为0(一般不需要改写)
*/
- (CGFloat)getDrawRunDescentHeight;
/**
* 获取绘画区域宽度(默认实现)
*/
- (CGFloat)getDrawRunWidth;
/**
* 释放内存 (一般不需要 已注释 需要在打开)
*/
//- (void)DrawRunDealloc;
@end
@@ -0,0 +1,217 @@
//
// TYDrawStorage.m
// TYAttributedLabelDemo
//
// Created by tanyang on 15/4/8.
// Copyright (c) 2015年 tanyang. All rights reserved.
//
#import "TYDrawStorage.h"
#import <CoreText/CoreText.h>
@interface TYDrawStorage (){
CGFloat _fontAscent;
CGFloat _fontDescent;
NSRange _fixRange;
}
@end
@implementation TYDrawStorage
#pragma mark - protocol
- (void)currentReplacedStringNum:(NSInteger)replacedStringNum
{
_fixRange = [self fixRange:_range replaceStringNum:replacedStringNum];
}
- (void)setTextfontAscent:(CGFloat)ascent descent:(CGFloat)descent;
{
_fontAscent = ascent;
_fontDescent = -descent;
}
- (void)addTextStorageWithAttributedString:(NSMutableAttributedString *)attributedString
{
NSRange range = _fixRange;
if (range.location == NSNotFound) {
return;
}else {
// 用空白替换
[attributedString replaceCharactersInRange:range withString:[self spaceReplaceString]];
// 修正range
range = NSMakeRange(range.location, 1);
_realRange = range;
}
// 设置合适的对齐
[self setAppropriateAlignment];
// 添加文本属性和runDelegate
[self addRunDelegateWithAttributedString:attributedString range:range];
}
- (NSAttributedString *)appendTextStorageAttributedString
{
// 创建空字符属性文本
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:[self spaceReplaceString]];
// 修正range
_range = NSMakeRange(0, 1);
// 设置合适的对齐
[self setAppropriateAlignment];
// 添加文本属性和runDelegate
[self addRunDelegateWithAttributedString:attributedString range:_range];
return attributedString;
}
- (void)drawStorageWithRect:(CGRect)rect
{
}
#pragma mark - public
- (CGFloat)getDrawRunAscentHeight
{
CGFloat ascent = 0;
CGFloat height = self.size.height+_margin.bottom+_margin.top;
switch (_drawAlignment)
{
case TYDrawAlignmentTop:
ascent = height - _fontDescent;
break;
case TYDrawAlignmentCenter:
{
CGFloat baseLine = (_fontAscent + _fontDescent) / 2 - _fontDescent;
ascent = height / 2 + baseLine;
break;
}
case TYDrawAlignmentBottom:
ascent = _fontAscent;
break;
default:
break;
}
return ascent;
}
- (CGFloat)getDrawRunWidth
{
return self.size.width+_margin.left+_margin.right;
}
- (CGFloat)getDrawRunDescentHeight
{
CGFloat descent = 0;
CGFloat height = self.size.height+_margin.bottom+_margin.top;
switch (_drawAlignment)
{
case TYDrawAlignmentTop:
descent = _fontDescent;
break;
case TYDrawAlignmentCenter:
{
CGFloat baseLine = (_fontAscent + _fontDescent) / 2 - _fontDescent;
descent = height / 2 - baseLine;
break;
}
case TYDrawAlignmentBottom:
descent = height - _fontAscent;
break;
default:
break;
}
return descent;
}
- (void)DrawRunDealloc
{
}
#pragma mark - private
- (NSString *)spaceReplaceString
{
// 替换字符
unichar objectReplacementChar = 0xFFFC;
NSString *objectReplacementString = [NSString stringWithCharacters:&objectReplacementChar length:1];
return objectReplacementString;
}
- (void)setAppropriateAlignment
{
// 判断size 大小 小于 _fontAscent 把对齐设为中心 更美观
if (_size.height <= _fontAscent + _fontDescent) {
_drawAlignment = TYDrawAlignmentCenter;
}
}
- (NSRange)fixRange:(NSRange)range replaceStringNum:(NSInteger)replaceStringNum
{
NSRange fixRange = range;
if (range.length <= 1 || replaceStringNum < 0)
return fixRange;
NSInteger location = range.location - replaceStringNum;
NSInteger length = range.length - replaceStringNum;
if (location < 0 && length > 0) {
fixRange = NSMakeRange(range.location, length);
}else if (location < 0 && length <= 0){
fixRange = NSMakeRange(NSNotFound, 0);
}else {
fixRange = NSMakeRange(range.location - replaceStringNum, range.length);
}
return fixRange;
}
// 添加文本属性和runDelegate
- (void)addRunDelegateWithAttributedString:(NSMutableAttributedString *)attributedString range:(NSRange)range
{
// 添加文本属性和runDelegate
[attributedString addAttribute:kTYTextRunAttributedName value:self range:range];
//为图片设置CTRunDelegate,delegate决定留给显示内容的空间大小
CTRunDelegateCallbacks runCallbacks;
runCallbacks.version = kCTRunDelegateVersion1;
runCallbacks.dealloc = TYTextRunDelegateDeallocCallback;
runCallbacks.getAscent = TYTextRunDelegateGetAscentCallback;
runCallbacks.getDescent = TYTextRunDelegateGetDescentCallback;
runCallbacks.getWidth = TYTextRunDelegateGetWidthCallback;
CTRunDelegateRef runDelegate = CTRunDelegateCreate(&runCallbacks, (__bridge void *)(self));
[attributedString addAttribute:(__bridge_transfer NSString *)kCTRunDelegateAttributeName value:(__bridge id)runDelegate range:range];
CFRelease(runDelegate);
}
//CTRun的回调,销毁内存的回调
void TYTextRunDelegateDeallocCallback( void* refCon ){
//TYDrawRun *textRun = (__bridge TYDrawRun *)refCon;
//[textRun DrawRunDealloc];
}
//CTRun的回调,获取高度
CGFloat TYTextRunDelegateGetAscentCallback( void *refCon ){
TYDrawStorage *drawStorage = (__bridge TYDrawStorage *)refCon;
return [drawStorage getDrawRunAscentHeight];
}
CGFloat TYTextRunDelegateGetDescentCallback(void *refCon){
TYDrawStorage *drawStorage = (__bridge TYDrawStorage *)refCon;
return [drawStorage getDrawRunDescentHeight];
}
//CTRun的回调,获取宽度
CGFloat TYTextRunDelegateGetWidthCallback(void *refCon){
TYDrawStorage *drawStorage = (__bridge TYDrawStorage *)refCon;
return [drawStorage getDrawRunWidth];
}
@end
@@ -0,0 +1,44 @@
//
// TYImageCache.h
// TYImageCache
//
// Created by tanyang on 25/08/15.
// Copyright (c) 2015 tanyang. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface TYImageCache : NSObject
@property (nonatomic, strong) NSString *localDirectory;
// 单例cache
+ (instancetype)cache;
// 清除cache
- (void)clearCache;
// 是否在本地找到图片,是否需要缩略图
- (void)imageForURL:(NSString *)imageURL needThumImage:(BOOL)needThumImage found:(void(^)(UIImage* image))found notFound:(void(^)(void))notFound;
// 是否在本地找到图片
- (void) imageForURL:(NSString *)imageURL found:(void(^)(UIImage* image))found notFound:(void(^)(void))notFound;
// 图片是否缓存
- (BOOL)imageIsCacheForURL:(NSString *)imageURL;
// 同步下载保存image
- (BOOL)saveImageFromURL:(NSString *)imageName thumbImageSize:(CGSize)thumbImageSize;
// 保存图片
- (BOOL)saveImageFromURL:(NSString *)imageName data:(NSData *)imageData;
// 保存image和缩略图
- (BOOL)saveImageFromURL:(NSString *)imageName thumbImageSize:(CGSize)thumbImageSize data:(NSData *)imageData;
// 异步下载保存image
- (void)saveAsyncImageFromURL:(NSString *)imageURL thumbImageSize:(CGSize)thumbImageSize completion:(void(^)(BOOL isCache))completionBlock;
// 异步下载保存image数组
- (void)saveAsyncImagesFromURLArray:(NSArray *)imageURLArray thumbImageSize:(CGSize)thumbImageSize completion:(void(^)(BOOL isCache))completionBlock;
@end
@@ -0,0 +1,295 @@
//
// DBImageViewCache.m
// DBImageView
//
// Created by iBo on 25/08/14.
// Copyright (c) 2014 Daniele Bogo. All rights reserved.
//
#import "TYImageCache.h"
#import <CommonCrypto/CommonDigest.h>
@interface TYImageCache (){
NSFileManager *_fileManager;
}
@end
@implementation TYImageCache
static TYImageCache *_instance;
+ (id)allocWithZone:(NSZone *)zone
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instance = [super allocWithZone:zone];
});
return _instance;
}
+ (instancetype)cache
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instance = [[self alloc] init];
});
return _instance;
}
#pragma mark - md5加密
+ (NSString *) md5:(NSString *)str
{
const char *cStr = [str UTF8String];
if (cStr == NULL) {
cStr = "";
}
unsigned char result[CC_MD5_DIGEST_LENGTH];
CC_MD5( cStr, (CC_LONG)strlen(cStr), result );
return [NSString stringWithFormat:
@"%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
result[0], result[1], result[2], result[3],
result[4], result[5], result[6], result[7],
result[8], result[9], result[10], result[11],
result[12], result[13], result[14], result[15]
];
}
- (id) init
{
self= [super init];
if ( self ) {
_fileManager = [NSFileManager new];
[self createLocalDirectory];
}
return self;
}
#pragma mark - 创建缓存图片文件目录
- (void) createLocalDirectory
{
if ( ![_fileManager fileExistsAtPath:self.localDirectory] ) {
NSError *error;
if ( ![[NSFileManager defaultManager] createDirectoryAtPath:self.localDirectory withIntermediateDirectories:YES attributes:nil error:&error] ) {
NSLog(@"[%@] ERROR: attempting to write create MyFolder directory", [self class]);
NSAssert( FALSE, @"Failed to create directory maybe out of disk space?");
}
}
}
#pragma mark - 存放image的文件夹路径
- (NSString *) localDirectory
{
if (_localDirectory == nil) {
_localDirectory = [NSString stringWithFormat:@"%@/ImageCache", NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)[0]];
}
return _localDirectory;
}
#pragma mark - image名字MD5加密后的路径
- (NSString *) pathOnDiskForName:(NSString *)imageName
{
return [self.localDirectory stringByAppendingPathComponent:[TYImageCache md5:imageName]];
}
#pragma mark - 保存image和缩略图
- (BOOL) saveImageFromURL:(NSString *)imageName thumbImageSize:(CGSize)thumbImageSize data:(NSData *)imageData
{
// 转换gif 到 image
UIImage *image = [UIImage imageWithData:imageData];
BOOL succeed = [self saveImageFromName:imageName image:image];
if (!CGSizeEqualToSize(thumbImageSize,CGSizeZero)) {
// 保存thumbImage
image = [self scaleImage:image ToSize:thumbImageSize];
succeed = [self saveImageFromName:[NSString stringWithFormat:@"Thumb%@",imageName] image:image];
}
return succeed;
}
- (BOOL)saveImageFromURL:(NSString *)imageName data:(NSData *)imageData
{
return [self saveImageFromURL:imageName thumbImageSize:CGSizeZero data:imageData];
}
#pragma mark - 保存image根据UImage
- (BOOL)saveImageFromName:(NSString *)imageName image:(UIImage *)image
{
if (!image) {
return NO;
}
if ([[imageName lowercaseString] hasSuffix:@".png"] || [[imageName lowercaseString] hasSuffix:@".bmp"]) {
// png图片
[UIImagePNGRepresentation(image) writeToFile:[self pathOnDiskForName:imageName] options:NSAtomicWrite error:nil];
return YES;
} else if ([[imageName lowercaseString] hasSuffix:@".jpg"] || [[imageName lowercaseString] hasSuffix:@".jpeg"] || [[imageName lowercaseString] hasSuffix:@".gif"])
{
//jpg图片
[UIImageJPEGRepresentation(image, 1.0) writeToFile:[self pathOnDiskForName:imageName] options:NSAtomicWrite error:nil];
return YES;
} else {
// 未知图片类型
NSLog(@"文件后缀名未知! CTImageCache ");
return NO;
}
}
#pragma mark - 从网络下载缓存image
// 同步下载保存image
- (BOOL)saveImageFromURL:(NSString *)imageName thumbImageSize:(CGSize)thumbImageSize
{
// 从网络上加载图片
NSData * data = [NSData dataWithContentsOfURL:[NSURL URLWithString:imageName]];
if (!data) {
return NO;
}
// 缓存图片
return [self saveImageFromURL:imageName thumbImageSize:thumbImageSize data:data];
}
// 异步下载保存image
- (void)saveAsyncImageFromURL:(NSString *)imageURL thumbImageSize:(CGSize)thumbImageSize completion:(void(^)(BOOL isCache))completionBlock
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 异步操作
// 从网络上 缓存图片
BOOL isCached = [self saveImageFromURL:imageURL thumbImageSize:thumbImageSize];
if (!completionBlock) {
return ;
}
dispatch_async(dispatch_get_main_queue(), ^{
// 主线程更新
completionBlock(isCached);
});
});
}
- (void)saveAsyncImagesFromURLArray:(NSArray *)imageURLArray thumbImageSize:(CGSize)thumbImageSize completion:(void (^)(BOOL))completionBlock
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 异步操作
// 从网络上 缓存图片
__block BOOL isCached = NO;
[imageURLArray enumerateObjectsUsingBlock:^(NSString *imageURL, NSUInteger idx, BOOL *stop) {
isCached = [self saveImageFromURL:imageURL thumbImageSize:thumbImageSize];
}];
if (!completionBlock) {
return ;
}
dispatch_async(dispatch_get_main_queue(), ^{
// 主线程更新
completionBlock(isCached);
});
});
}
// 同步获取image
- (UIImage *)imageForURL:(NSString *)imageURL
{
NSString *path = [self pathOnDiskForName:imageURL];
return [[UIImage alloc] initWithContentsOfFile:path];
}
#pragma mark - image是否存在
- (void) imageForURL:(NSString *)imageURL found:(void(^)(UIImage* image))found notFound:(void(^)(void))notFound
{
[self imageForURL:imageURL needThumImage:NO found:found notFound:notFound];
}
#pragma mark - image是否存在,是否需要缩略图,如果知道返回缩略图 否则返回原图
- (void) imageForURL:(NSString *)imageURL needThumImage:(BOOL)needThumImage found:(void(^)(UIImage* image))found notFound:(void(^)(void))notFound
{
if ( !imageURL ) {
return;
}
NSString *imageName = imageURL;
if (needThumImage ) {
imageName = [NSString stringWithFormat:@"Thumb%@",imageURL];
}
// 图片路径
UIImage* image = [self imageForURL:imageName];
if (!image && needThumImage) {
image = [self imageForURL:imageURL];
}
if (image) {
found(image);
}else {
notFound();
}
}
#pragma mark - 是否缓存图片
- (BOOL) imageIsCacheForURL:(NSString *)imageURL {
return [_fileManager fileExistsAtPath:[self pathOnDiskForName:imageURL]];
}
#pragma mark - 清除内存
- (void) clearCache
{
NSError *error;
[_fileManager removeItemAtPath:self.localDirectory error:&error];
if ( ![_fileManager createDirectoryAtPath:self.localDirectory withIntermediateDirectories:NO attributes:nil error:&error] )
return;
}
#pragma mark - scale image
// 返回适应到targetSize的合适图片image
- (UIImage *)scaleImage:(UIImage *)sourceImage ToSize:(CGSize)targetSize
{
CGFloat width = sourceImage.size.width;
CGFloat height = sourceImage.size.height;
CGFloat targetWidth = targetSize.width;
CGFloat targetHeight = targetSize.height;
CGFloat scaleFactor = 0.0;
CGFloat scaledWidth = targetWidth;
CGFloat scaledHeight = targetHeight;
CGPoint thumbnailPoint = CGPointMake(0.0,0.0);
if (CGSizeEqualToSize(sourceImage.size, targetSize) == NO) {
CGFloat widthFactor = targetWidth / width;
CGFloat heightFactor = targetHeight / height;
if (widthFactor < heightFactor)
scaleFactor = widthFactor;
else
scaleFactor = heightFactor;
scaledWidth = width * scaleFactor;
scaledHeight = height * scaleFactor;
// center the image
if (widthFactor < heightFactor) {
thumbnailPoint.y = (targetHeight - scaledHeight) * 0.5;
} else if (widthFactor > heightFactor) {
thumbnailPoint.x = (targetWidth - scaledWidth) * 0.5;
}
}
// this is actually the interesting part:
UIGraphicsBeginImageContext(targetSize);
CGRect thumbnailRect = CGRectZero;
thumbnailRect.origin = thumbnailPoint;
thumbnailRect.size.width = scaledWidth;
thumbnailRect.size.height = scaledHeight;
[sourceImage drawInRect:thumbnailRect];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
if(newImage == nil)
NSLog(@"could not scale image");
return newImage ;
}
@end
@@ -0,0 +1,31 @@
//
// TYDrawImageStorage.h
// TYAttributedLabelDemo
//
// Created by tanyang on 15/4/8.
// Copyright (c) 2015年 tanyang. All rights reserved.
//
#import "TYDrawStorage.h"
typedef enum : NSUInteger {
TYImageAlignmentCenter, // 图片居中
TYImageAlignmentLeft, // 图片左对齐
TYImageAlignmentRight, // 图片右对齐
TYImageAlignmentFill // 图片拉伸填充
} TYImageAlignment;
@interface TYImageStorage : TYDrawStorage<TYViewStorageProtocol>
@property (nonatomic, strong) UIImage *image;
@property (nonatomic, strong) NSString *imageName;
@property (nonatomic, strong) NSURL *imageURL;
@property (nonatomic, strong) NSString *placeholdImageName;
@property (nonatomic, assign) TYImageAlignment imageAlignment; // default center
@property (nonatomic, assign) BOOL cacheImageOnMemory; // default NO ,if YES can improve performancebut increase memory
@end
@@ -0,0 +1,120 @@
//
// TYDrawImageStorage.m
// TYAttributedLabelDemo
//
// Created by tanyang on 15/4/8.
// Copyright (c) 2015年 tanyang. All rights reserved.
//
#import "TYImageStorage.h"
#import "TYImageCache.h"
@interface TYImageStorage ()
@property (nonatomic, weak) UIView *ownerView;
@property (nonatomic, assign) BOOL isNeedUpdateFrame;
@end
@implementation TYImageStorage
- (instancetype)init
{
if (self = [super init]) {
_cacheImageOnMemory = NO;
}
return self;
}
#pragma mark - protocol
- (void)setOwnerView:(UIView *)ownerView
{
_ownerView = ownerView;
if (!ownerView || !_imageURL) {
return;
}
if ([_imageURL isKindOfClass:[NSURL class]]
&& ![[TYImageCache cache] imageIsCacheForURL:_imageURL.absoluteString]) {
[[TYImageCache cache]saveAsyncImageFromURL:_imageURL.absoluteString thumbImageSize:self.size completion:^(BOOL isCache) {
if (self.isNeedUpdateFrame) {
if (ownerView && isCache) {
[ownerView setNeedsDisplay];
}
self.isNeedUpdateFrame = NO;
}
}];
}
}
- (void)drawStorageWithRect:(CGRect)rect
{
__block UIImage *image = nil;
if (_image) {
// 本地图片名
image = _image;
}else if (_imageName){
// 图片网址
image = [UIImage imageNamed:_imageName];
if (_cacheImageOnMemory) {
_image = image;
}
} else if (_imageURL){
// 图片数据
[[TYImageCache cache] imageForURL:_imageURL.absoluteString needThumImage:NO found:^(UIImage *loaceImage) {
image = loaceImage;
if (self.cacheImageOnMemory) {
self.image = image;
}
} notFound:^{
image = self.placeholdImageName ? [UIImage imageNamed:self.placeholdImageName] : nil;
self.isNeedUpdateFrame = YES;
}];
}
if (image) {
CGRect fitRect = [self rectFitOriginSize:image.size byRect:rect];
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextDrawImage(context, fitRect, image.CGImage);
}
}
- (CGRect)rectFitOriginSize:(CGSize)size byRect:(CGRect)byRect{
if (_imageAlignment == TYImageAlignmentFill) {
return byRect;
}
CGRect scaleRect = byRect;
CGFloat targetWidth = byRect.size.width;
CGFloat targetHeight = byRect.size.height;
CGFloat widthFactor = targetWidth / size.width;
CGFloat heightFactor = targetHeight / size.height;
CGFloat scaleFactor = MIN(widthFactor, heightFactor);
CGFloat scaledWidth = size.width * scaleFactor;
CGFloat scaledHeight = size.height * scaleFactor;
scaleRect.size = CGSizeMake(scaledWidth, scaledHeight);
// center the image
if (widthFactor < heightFactor) {
scaleRect.origin.y += (targetHeight - scaledHeight) * 0.5;
} else if (widthFactor > heightFactor) {
switch (_imageAlignment) {
case TYImageAlignmentCenter:
scaleRect.origin.x += (targetWidth - scaledWidth) * 0.5;
break;
case TYImageAlignmentRight:
scaleRect.origin.x += (targetWidth - scaledWidth);
default:
break;
}
}
return scaleRect;
}
// override
- (void)didNotDrawRun
{
}
@end
@@ -0,0 +1,20 @@
//
// TYLinkTextStorage.h
// TYAttributedLabelDemo
//
// Created by tanyang on 15/4/8.
// Copyright (c) 2015年 tanyang. All rights reserved.
//
#import "TYTextStorage.h"
@interface TYLinkTextStorage : TYTextStorage<TYLinkStorageProtocol>
// textColor 链接颜色 如未设置就是TYAttributedLabel的linkColor
// TYAttributedLabel的 highlightedLinkBackgroundColor 高亮背景颜色
// underLineStyle 下划线样式(无,单 双) 默认单
// modifier 下划线样式 (点 线)默认线
@property (nonatomic, strong) id linkData; // 链接携带的数据
@end
@@ -0,0 +1,32 @@
//
// TYLinkTextStorage.m
// TYAttributedLabelDemo
//
// Created by tanyang on 15/4/8.
// Copyright (c) 2015年 tanyang. All rights reserved.
//
#import "TYLinkTextStorage.h"
@implementation TYLinkTextStorage
- (instancetype)init
{
if (self = [super init]) {
self.underLineStyle = kCTUnderlineStyleSingle;
self.modifier = kCTUnderlinePatternSolid;
}
return self;
}
#pragma mark - protocol
- (void)addTextStorageWithAttributedString:(NSMutableAttributedString *)attributedString
{
[super addTextStorageWithAttributedString:attributedString];
[attributedString addAttribute:kTYTextRunAttributedName value:self range:self.range];
self.text = [attributedString.string substringWithRange:self.range];
}
@end
@@ -0,0 +1,197 @@
//
// TYTextContainer+Extended.m
// TYAttributedLabelDemo
//
// Created by tanyang on 15/6/7.
// Copyright (c) 2015年 tanyang. All rights reserved.
//
#import "TYTextContainer.h"
@implementation TYTextContainer (Link)
#pragma mark - addLink
- (void)addLinkWithLinkData:(id)linkData range:(NSRange)range
{
[self addLinkWithLinkData:linkData linkColor:nil range:range];
}
- (void)addLinkWithLinkData:(id)linkData linkColor:(UIColor *)linkColor range:(NSRange )range;
{
[self addLinkWithLinkData:linkData linkColor:linkColor underLineStyle:kCTUnderlineStyleSingle range:range];
}
- (void)addLinkWithLinkData:(id)linkData linkColor:(UIColor *)linkColor underLineStyle:(CTUnderlineStyle)underLineStyle range:(NSRange )range
{
TYLinkTextStorage *linkTextStorage = [[TYLinkTextStorage alloc] init];
linkTextStorage.range = range;
linkTextStorage.textColor = linkColor;
linkTextStorage.linkData = linkData;
linkTextStorage.underLineStyle = underLineStyle;
[self addTextStorage:linkTextStorage];
}
#pragma mark - appendLink
- (void)appendLinkWithText:(NSString *)linkText linkFont:(UIFont *)linkFont linkData:(id)linkData
{
[self appendLinkWithText:linkText linkFont:linkFont linkColor:nil linkData:linkData];
}
- (void)appendLinkWithText:(NSString *)linkText linkFont:(UIFont *)linkFont linkColor:(UIColor *)linkColor linkData:(id)linkData
{
[self appendLinkWithText:linkText linkFont:linkFont linkColor:linkColor underLineStyle:kCTUnderlineStyleSingle linkData:linkData];
}
- (void)appendLinkWithText:(NSString *)linkText linkFont:(UIFont *)linkFont linkColor:(UIColor *)linkColor underLineStyle:(CTUnderlineStyle)underLineStyle linkData:(id)linkData
{
TYLinkTextStorage *linkTextStorage = [[TYLinkTextStorage alloc] init];
linkTextStorage.text = linkText;
linkTextStorage.font = linkFont;
linkTextStorage.textColor = linkColor;
linkTextStorage.linkData = linkData;
linkTextStorage.underLineStyle = underLineStyle;
[self appendTextStorage:linkTextStorage];
}
@end
@implementation TYTextContainer (UIImage)
#pragma mark addImage
- (void)addImageContent:(id)imageContent range:(NSRange)range size:(CGSize)size alignment:(TYDrawAlignment)alignment
{
TYImageStorage *imageStorage = [[TYImageStorage alloc] init];
if ([imageContent isKindOfClass:[UIImage class]]) {
imageStorage.image = imageContent;
}else if ([imageContent isKindOfClass:[NSString class]]){
imageStorage.imageName = imageContent;
} else {
return;
}
imageStorage.drawAlignment = alignment;
imageStorage.range = range;
imageStorage.size = size;
[self addTextStorage:imageStorage];
}
- (void)addImage:(UIImage *)image range:(NSRange)range size:(CGSize)size alignment:(TYDrawAlignment)alignment
{
[self addImageContent:image range:range size:size alignment:alignment];
}
- (void)addImage:(UIImage *)image range:(NSRange)range size:(CGSize)size
{
[self addImage:image range:range size:size alignment:TYDrawAlignmentTop];
}
- (void)addImage:(UIImage *)image range:(NSRange)range
{
[self addImage:image range:range size:image.size];
}
- (void)addImageWithName:(NSString *)imageName range:(NSRange)range size:(CGSize)size alignment:(TYDrawAlignment)alignment
{
[self addImageContent:imageName range:range size:size alignment:alignment];
}
- (void)addImageWithName:(NSString *)imageName range:(NSRange)range size:(CGSize)size
{
[self addImageWithName:imageName range:range size:size alignment:TYDrawAlignmentTop];
}
- (void)addImageWithName:(NSString *)imageName range:(NSRange)range
{
[self addImageWithName:imageName range:range size:CGSizeMake(self.font.pointSize, self.font.ascender)];
}
#pragma mark - appendImage
- (void)appendImageContent:(id)imageContent size:(CGSize)size alignment:(TYDrawAlignment)alignment
{
TYImageStorage *imageStorage = [[TYImageStorage alloc] init];
if ([imageContent isKindOfClass:[UIImage class]]) {
imageStorage.image = imageContent;
}else if ([imageContent isKindOfClass:[NSString class]]){
imageStorage.imageName = imageContent;
} else {
return;
}
imageStorage.drawAlignment = alignment;
imageStorage.size = size;
[self appendTextStorage:imageStorage];
}
- (void)appendImage:(UIImage *)image size:(CGSize)size alignment:(TYDrawAlignment)alignment
{
[self appendImageContent:image size:size alignment:alignment];
}
- (void)appendImage:(UIImage *)image size:(CGSize)size
{
[self appendImage:image size:size alignment:TYDrawAlignmentTop];
}
- (void)appendImage:(UIImage *)image
{
[self appendImage:image size:image.size];
}
- (void)appendImageWithName:(NSString *)imageName size:(CGSize)size alignment:(TYDrawAlignment)alignment
{
[self appendImageContent:imageName size:size alignment:alignment];
}
- (void)appendImageWithName:(NSString *)imageName size:(CGSize)size
{
[self appendImageWithName:imageName size:size alignment:TYDrawAlignmentTop];
}
- (void)appendImageWithName:(NSString *)imageName
{
[self appendImageWithName:imageName size:CGSizeMake(self.font.pointSize, self.font.ascender)];
}
@end
@implementation TYTextContainer (UIView)
#pragma mark - addView
- (void)addView:(UIView *)view range:(NSRange)range alignment:(TYDrawAlignment)alignment
{
TYViewStorage *viewStorage = [[TYViewStorage alloc] init];
viewStorage.drawAlignment = alignment;
viewStorage.view = view;
viewStorage.range = range;
[self addTextStorage:viewStorage];
}
- (void)addView:(UIView *)view range:(NSRange)range
{
[self addView:view range:range alignment:TYDrawAlignmentTop];
}
#pragma mark - appendView
- (void)appendView:(UIView *)view alignment:(TYDrawAlignment)alignment
{
TYViewStorage *viewStorage = [[TYViewStorage alloc] init];
viewStorage.drawAlignment = alignment;
viewStorage.view = view;
[self appendTextStorage:viewStorage];
}
- (void)appendView:(UIView *)view
{
[self appendView:view alignment:TYDrawAlignmentTop];
}
@end
@@ -0,0 +1,297 @@
//
// TYTextContainer.h
// TYAttributedLabelDemo
//
// Created by tanyang on 15/6/4.
// Copyright (c) 2015年 tanyang. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "TYTextStorageProtocol.h"
#import "NSMutableAttributedString+TY.h"
#import "TYTextStorage.h"
#import "TYLinkTextStorage.h"
#import "TYDrawStorage.h"
#import "TYImageStorage.h"
#import "TYViewStorage.h"
@interface TYTextContainer : NSObject
@property (nonatomic, strong) NSString *text;
@property (nonatomic, strong) NSAttributedString *attributedText;
@property (nonatomic, assign) NSInteger numberOfLines; //行数
@property (nonatomic, strong) UIColor *textColor; // 文字颜色
@property (nonatomic, strong) UIColor *linkColor; //链接颜色
@property (nonatomic, strong) UIFont *font; // 文字大小
@property (nonatomic, assign) unichar strokeWidth; // 空心字边框宽
@property (nonatomic, strong) UIColor *strokeColor; // 空心字边框颜色
@property (nonatomic, assign) unichar characterSpacing; // 字距
@property (nonatomic, assign) CGFloat linesSpacing; // 行距
@property (nonatomic, assign) CGFloat paragraphSpacing; // 段落间距
@property (nonatomic, assign) CTTextAlignment textAlignment; // 文本对齐方式 kCTTextAlignmentLeft
@property (nonatomic, assign) CTLineBreakMode lineBreakMode; // 换行模式 kCTLineBreakByCharWrapping
@property (nonatomic, assign) BOOL isWidthToFit; // 宽度自适应
// after createTextContainer, have value
@property (nonatomic, assign, readonly) CGFloat textHeight;
@property (nonatomic, assign, readonly) CGFloat textWidth;
// after createTextContainer, have value
@property (nonatomic, strong, readonly) NSArray *textStorages;
/**
* 生成文本容器textContainer
*/
- (instancetype)createTextContainerWithTextWidth:(CGFloat)textWidth;
- (instancetype)createTextContainerWithContentSize:(CGSize)contentSize;
/**
* 生成属性字符串
*/
- (NSAttributedString *)createAttributedString;
/**
* 获取文本的size
*/
- (CGSize)getSuggestedSizeWithFramesetter:(CTFramesetterRef)framesetter width:(CGFloat)width;
/**
* 获取文本高度
*/
- (CGFloat)getHeightWithFramesetter:(CTFramesetterRef)framesetter width:(CGFloat)width;
@end
@interface TYTextContainer (Add)
/**
* 添加 textStorage (自定义显示内容)
*
* @param textStorage 自定义
*/
- (void)addTextStorage:(id<TYTextStorageProtocol>)textStorage;
/**
* 添加 textRun数组 (自定义显示内容)
*
*/
- (void)addTextStorageArray:(NSArray *)textStorageArray;
@end
#pragma mark - append
@interface TYTextContainer (Append)
/**
* 追加(添加到最后) 普通文本
*
* @param text 普通文本
*/
- (void)appendText:(NSString *)text;
/**
* 追加(添加到最后) 属性文本
*
* @param attributedText 属性文本
*/
- (void)appendTextAttributedString: (NSAttributedString *)attributedText;
/**
* 追加(添加到最后) textStorage (自定义显示内容)
*
* @param textStorage 自定义Storage(自定义显示内容)
*/
- (void)appendTextStorage:(id<TYAppendTextStorageProtocol>)textStorage;
/**
* 追加(添加到最后) textStorage 数组
*
* @param textStorageArray 自定义run数组(需遵循协议TYAppendTextStorageProtocol,否则不会添加)
*/
- (void)appendTextStorageArray:(NSArray *)textStorageArray;
@end
#pragma mark - Link
@interface TYTextContainer (Link)
/**
* 添加 链接LinkTextStorage
*/
- (void)addLinkWithLinkData:(id)linkData range:(NSRange )range;
/**
* 添加 链接LinkTextStorage
*/
- (void)addLinkWithLinkData:(id)linkData linkColor:(UIColor *)linkColor range:(NSRange )range;
/**
* 添加 链接LinkTextStorage
*
* @param linkData 链接携带的数据
* @param linkColor 链接颜色
* @param underLineStyle 下划线样式(无,单 双) 默认单
* @param range 范围
*/
- (void)addLinkWithLinkData:(id)linkData linkColor:(UIColor *)linkColor underLineStyle:(CTUnderlineStyle)underLineStyle range:(NSRange )range;
/**
* 追加 链接LinkTextStorage
*/
- (void)appendLinkWithText:(NSString *)linkText linkFont:(UIFont *)linkFont linkData:(id)linkData;
/**
* 追加 链接LinkTextStorage
*/
- (void)appendLinkWithText:(NSString *)linkText linkFont:(UIFont *)linkFont linkColor:(UIColor *)linkColor linkData:(id)linkData;
/**
* 追加 链接LinkTextStorage
*
* @param linkText 链接文本
* @param linkData 链接携带的数据
* @param underLineStyle 下划线样式(无,单 双) 默认单
*/
- (void)appendLinkWithText:(NSString *)linkText linkFont:(UIFont *)linkFont linkColor:(UIColor *)linkColor underLineStyle:(CTUnderlineStyle)underLineStyle linkData:(id)linkData;
@end
#pragma mark - 扩展支持UIImage
@interface TYTextContainer (UIImage)
#pragma mark - addImageStorage
/**
* 添加 imageStorage image数据
*/
- (void)addImage:(UIImage *)image range:(NSRange)range;
/**
* 添加 imageStorage image数据
*/
- (void)addImage:(UIImage *)image range:(NSRange)range size:(CGSize)size;
/**
* 添加 imageStorage image数据
*
* @param image image
* @param range 所在文本位置
* @param size 图片大小
* @param alignment 图片对齐方式
*/
- (void)addImage:(UIImage *)image range:(NSRange)range
size:(CGSize)size
alignment: (TYDrawAlignment)alignment;
/**
* 添加 imageStorage image数据
*/
- (void)addImageWithName:(NSString *)imageName range:(NSRange)range;
/**
* 添加 imageStorage image数据
*/
- (void)addImageWithName:(NSString *)imageName
range:(NSRange)range
size:(CGSize)size;
/**
* 添加 imageStorage image数据
*
* @param imageName image名
* @param range 所在文本位置
* @param size 图片大小
* @param alignment 图片对齐方式
*/
- (void)addImageWithName:(NSString *)imageName
range:(NSRange)range
size:(CGSize)size
alignment:(TYDrawAlignment)alignment;
#pragma mark - appendImageStorage
/**
* 追加 imageStorage image数据
*/
- (void)appendImage:(UIImage *)image;
/**
* 追加 imageStorage image数据
*/
- (void)appendImage:(UIImage *)image
size:(CGSize)size;
/**
* 追加 imageStorage image数据
*
* @param image image
* @param size 图片大小
* @param alignment 图片对齐
*/
- (void)appendImage:(UIImage *)image
size:(CGSize)size
alignment:(TYDrawAlignment)alignment;
/**
* 追加 imageStorage image数据
*/
- (void)appendImageWithName:(NSString *)imageName;
/**
* 追加 imageStorage image数据
*/
- (void)appendImageWithName:(NSString *)imageName size:(CGSize)size;
/**
* 追加 imageStorage image数据
*
* @param imageName imageName
* @param size 图片大小
* @param alignment 图片对齐
*/
- (void)appendImageWithName:(NSString *)imageName
size:(CGSize)size
alignment:(TYDrawAlignment)alignment;
@end
#pragma mark - 扩展支持UIView
@interface TYTextContainer (UIView)
/**
* 添加 viewStorage (添加 UI控件 需要设置frame)
*/
- (void)addView:(UIView *)view range:(NSRange)range;
/**
* 添加 viewStorage (添加 UI控件 需要设置frame)
*
* @param view UIView (UI控件)
* @param range 所在文本位置
* @param alignment view对齐方式
*/
- (void)addView:(UIView *)view
range:(NSRange)range
alignment:(TYDrawAlignment)alignment;
/**
* 追加 viewStorage (添加 UI控件 需要设置frame)
*/
- (void)appendView:(UIView *)view;
/**
* 追加 viewStorage (添加 UI控件 需要设置frame)
*
* @param view UIView (UI控件)
* @param alignment view对齐
*/
- (void)appendView:(UIView *)view alignment:(TYDrawAlignment)alignment;
@end
@@ -0,0 +1,678 @@
//
// 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
@@ -0,0 +1,24 @@
//
// TYTextStorage.h
// TYAttributedLabelDemo
//
// Created by tanyang on 15/4/8.
// Copyright (c) 2015年 tanyang. All rights reserved.
//
#import "TYTextStorageProtocol.h"
#import <CoreText/CoreText.h>
@interface TYTextStorage : NSObject<TYAppendTextStorageProtocol>
@property (nonatomic, assign) NSInteger tag; // 标识
@property (nonatomic, assign) NSRange range; //如果appendStorage, range只针对追加的文本
@property (nonatomic, assign) NSRange realRange; // label文本中实际位置,因为某些文本被替换,会导致位置偏移
@property (nonatomic, strong) NSString *text; // 只针对追加text文本
@property (nonatomic, strong) UIColor *textColor; // 文本颜色
@property (nonatomic, strong) UIFont *font; // 字体
@property (nonatomic, assign) CTUnderlineStyle underLineStyle;// 下划线样式(单 双)(默认没有)
@property (nonatomic, assign) CTUnderlineStyleModifiers modifier;// 下划线样式 (点 线)(默认线)
@end
@@ -0,0 +1,47 @@
//
// TYTextStorage.m
// TYAttributedLabelDemo
//
// Created by tanyang on 15/4/8.
// Copyright (c) 2015年 tanyang. All rights reserved.
//
#import "TYTextStorage.h"
#import "NSMutableAttributedString+TY.h"
@implementation TYTextStorage
#pragma mark - protocol
- (void)addTextStorageWithAttributedString:(NSMutableAttributedString *)attributedString
{
// 颜色
if (_textColor) {
[attributedString addAttributeTextColor:_textColor range:_range];
}
// 字体
if (_font) {
[attributedString addAttributeFont:_font range:_range];
}
// 下划线
if (_underLineStyle) {
[attributedString addAttributeUnderlineStyle:_underLineStyle modifier:_modifier range:_range];
}
}
- (NSAttributedString *)appendTextStorageAttributedString
{
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:_text];
// 验证范围
if (NSEqualRanges(_range, NSMakeRange(0, 0))) {
_range = NSMakeRange(0, attributedString.length);
}
[self addTextStorageWithAttributedString:attributedString];
return [attributedString copy];
}
@end
@@ -0,0 +1,94 @@
//
// TYTextStorageProtocol.h
// TYAttributedLabelDemo
//
// Created by tanyang on 15/4/8.
// Copyright (c) 2015年 tanyang. All rights reserved.
//
#import <UIKit/UIKit.h>
typedef enum : NSUInteger {
TYDrawAlignmentTop, // 底部齐平 向上伸展
TYDrawAlignmentCenter, // 中心齐平
TYDrawAlignmentBottom, // 顶部齐平 向下伸展
} TYDrawAlignment;
extern NSString *const kTYTextRunAttributedName;
@protocol TYTextStorageProtocol <NSObject>
@required
/**
* 范围(如果是appendStorage,range只针对追加的文本)
*/
@property (nonatomic,assign) NSRange range;
/**
* 文本中实际位置,因为某些文本被替换,会导致位置偏移
*/
@property (nonatomic,assign) NSRange realRange;
/**
* 添加属性到全文attributedString addTextStorage调用
*
* @param attributedString 属性字符串
*/
- (void)addTextStorageWithAttributedString:(NSMutableAttributedString *)attributedString;
@end
@protocol TYAppendTextStorageProtocol <TYTextStorageProtocol>
@required
/**
* 追加attributedString属性 appendTextStorage调用
*
* @return 返回需要追加的attributedString属性
*/
- (NSAttributedString *)appendTextStorageAttributedString;
@end
@protocol TYLinkStorageProtocol <TYAppendTextStorageProtocol>
@property (nonatomic, strong) UIColor *textColor; // 文本颜色
@end
@protocol TYDrawStorageProtocol <TYAppendTextStorageProtocol>
@property (nonatomic, assign) UIEdgeInsets margin; // 四周间距
/**
* 添加View 或 绘画 到该区域
*
* @param rect 绘画区域
*/
- (void)drawStorageWithRect:(CGRect)rect;
/**
* 设置字体高度 当前字符串替换数
*/
- (void)setTextfontAscent:(CGFloat)ascent descent:(CGFloat)descent;
// 当前替换字符数
- (void)currentReplacedStringNum:(NSInteger)replacedStringNum;
@end
@protocol TYViewStorageProtocol <NSObject>
/**
* 设置所属的view
*/
- (void)setOwnerView:(UIView *)ownerView;
/**
* 不会把你绘画出来
*/
- (void)didNotDrawRun;
@end
@@ -0,0 +1,15 @@
//
// TYDrawViewStorage.h
// TYAttributedLabelDemo
//
// Created by tanyang on 15/4/9.
// Copyright (c) 2015年 tanyang. All rights reserved.
//
#import "TYDrawStorage.h"
@interface TYViewStorage : TYDrawStorage<TYViewStorageProtocol>
@property (nonatomic, strong) UIView *view; // 添加view
@end
@@ -0,0 +1,66 @@
//
// TYDrawViewStorage.m
// TYAttributedLabelDemo
//
// Created by tanyang on 15/4/9.
// Copyright (c) 2015年 tanyang. All rights reserved.
//
#import "TYViewStorage.h"
@interface TYViewStorage ()
@property (nonatomic, weak) UIView *superView;
@end
@implementation TYViewStorage
#pragma mark - protocol
- (void)setView:(UIView *)view
{
_view = view;
if (CGSizeEqualToSize(self.size, CGSizeZero)) {
if ([NSThread isMainThread]) {
self.size = view.frame.size;
} else {
dispatch_semaphore_t signal = dispatch_semaphore_create(0);
dispatch_sync(dispatch_get_main_queue(), ^{
self.size = view.frame.size;
dispatch_semaphore_signal(signal);
});
dispatch_semaphore_wait(signal, DISPATCH_TIME_FOREVER);
}
}
}
- (void)setOwnerView:(UIView *)ownerView
{
if (_view.superview) {
[_view removeFromSuperview];
}
_superView = ownerView;
}
- (void)didNotDrawRun
{
[_view removeFromSuperview];
}
- (void)drawStorageWithRect:(CGRect)rect
{
if (_view == nil || _superView == nil) return;
// 设置frame 注意 转换rect CoreText context coordinates are the opposite to UIKit so we flip the bounds
CGAffineTransform transform = CGAffineTransformScale(CGAffineTransformMakeTranslation(0, _superView.bounds.size.height), 1.f, -1.f);
rect = CGRectApplyAffineTransform(rect, transform);
[_view setFrame:rect];
[_superView addSubview:_view];
}
- (void)dealloc {
[self.view performSelectorOnMainThread:@selector(removeFromSuperview) withObject:nil waitUntilDone:[NSThread isMainThread]];
}
@end