394 lines
14 KiB
Objective-C
394 lines
14 KiB
Objective-C
//
|
||
// UMComMutiStyleTextView.m
|
||
// UMCommunity
|
||
//
|
||
// Created by umeng on 15-3-5.
|
||
// Copyright (c) 2015年 Umeng. All rights reserved.
|
||
//
|
||
|
||
#import "UMComMutiStyleTextView.h"
|
||
#import "UMComResouceDefines.h"
|
||
#import <UMComFoundation/UMComKit+Color.h>
|
||
|
||
|
||
@interface UMComMutiStyleTextView ()
|
||
|
||
|
||
@end
|
||
|
||
@implementation UMComMutiStyleTextView
|
||
|
||
- (id)init
|
||
{
|
||
self = [self initWithFrame:CGRectZero];
|
||
return self;
|
||
}
|
||
|
||
- (void)dealloc
|
||
{
|
||
if (_framesetterRef) {
|
||
CFRelease((__bridge_retained CTFramesetterRef)(_framesetterRef));
|
||
_framesetterRef = NULL;
|
||
}
|
||
}
|
||
|
||
|
||
- (id)initWithFrame:(CGRect)frame
|
||
{
|
||
self = [super initWithFrame:frame];
|
||
if (self) {
|
||
[self createDefault];
|
||
}
|
||
return self;
|
||
}
|
||
|
||
- (void)setMutiStyleTextViewWithMutiText:(UMComMutiText *)mutiText
|
||
{
|
||
self.attributedText = mutiText.attributedText;
|
||
self.framesetterRef = mutiText.framesetterRef;
|
||
self.runs = mutiText.runs;
|
||
self.font = mutiText.font;
|
||
self.lineSpace = mutiText.lineSpace;
|
||
self.textColor = mutiText.textColor;
|
||
[self setText:mutiText.text];
|
||
}
|
||
|
||
#pragma mark - CreateDefault
|
||
|
||
- (void)createDefault
|
||
{
|
||
//public
|
||
_text = nil;
|
||
_font = [UIFont systemFontOfSize:13.0f];
|
||
_textColor = [UIColor blackColor];
|
||
_lineSpace = 2.0f;
|
||
// self.heightOffset = 0.0f;
|
||
_attributedText = nil;
|
||
_pointOffset = CGPointZero;
|
||
//private
|
||
_runs = [NSMutableArray array];
|
||
_runRectDictionary = [NSMutableDictionary dictionary];
|
||
_touchRun = nil;
|
||
_checkWords = [NSMutableArray arrayWithCapacity:1];
|
||
_framesetterRef = NULL;
|
||
|
||
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(tapInCerrentView:)];
|
||
[self addGestureRecognizer:tap];
|
||
}
|
||
|
||
|
||
|
||
#pragma mark - Set
|
||
- (void)setText:(NSString *)text
|
||
{
|
||
[self setNeedsDisplay];
|
||
|
||
_text = text;
|
||
}
|
||
|
||
|
||
- (void)awakeFromNib
|
||
{
|
||
[self createDefault];
|
||
|
||
}
|
||
|
||
#pragma mark - Draw Rect
|
||
|
||
- (void)drawRect:(CGRect)rect
|
||
{
|
||
//绘图上下文
|
||
CGContextRef contextRef = UIGraphicsGetCurrentContext();
|
||
if (self.attributedText == nil || self.attributedText.length == 0 ){
|
||
return;
|
||
}
|
||
|
||
CGRect viewRect = CGRectMake(self.pointOffset.x, -self.pointOffset.y, rect.size.width, rect.size.height);//
|
||
//修正坐标系
|
||
CGAffineTransform affineTransform = CGAffineTransformIdentity;
|
||
affineTransform = CGAffineTransformMakeTranslation(0.0, viewRect.size.height);
|
||
affineTransform = CGAffineTransformScale(affineTransform, 1.0, -1.0);
|
||
CGContextConcatCTM(contextRef, affineTransform);
|
||
|
||
//创建一个用来描画文字的路径,其区域为viewRect CGPath
|
||
CGMutablePathRef pathRef = CGPathCreateMutable();
|
||
CGPathAddRect(pathRef, NULL, viewRect);
|
||
|
||
CTFramesetterRef frameSetter = CFRetain((__bridge CTFramesetterRef)(self.framesetterRef));
|
||
CTFrameRef frameRef = CTFramesetterCreateFrame(frameSetter, CFRangeMake(0, 0), pathRef, nil);
|
||
//通过context在frame中描画文字内容
|
||
CTFrameDraw(frameRef, contextRef);
|
||
[self.runRectDictionary removeAllObjects];
|
||
[self setRunsKeysToRunRectWithFrameRef:frameRef];
|
||
CGPathRelease(pathRef);
|
||
CFRelease(frameRef);
|
||
CFRelease(frameSetter);
|
||
}
|
||
|
||
- (void)setRunsKeysToRunRectWithFrameRef:(CTFrameRef)frameRef
|
||
{
|
||
CFArrayRef lines = CTFrameGetLines(frameRef);
|
||
CGPoint lineOrigins[CFArrayGetCount(lines)];
|
||
CTFrameGetLineOrigins(frameRef, CFRangeMake(0, 0), lineOrigins);
|
||
|
||
for (int i = 0; i < CFArrayGetCount(lines); i++)
|
||
{
|
||
CTLineRef lineRef= CFArrayGetValueAtIndex(lines, i);
|
||
CGFloat lineAscent;
|
||
CGFloat lineDescent;
|
||
CGFloat lineLeading;
|
||
CGPoint lineOrigin = CGPointMake(lineOrigins[i].x, lineOrigins[i].y);//
|
||
CTLineGetTypographicBounds(lineRef, &lineAscent, &lineDescent, &lineLeading);
|
||
CFArrayRef runs = CTLineGetGlyphRuns(lineRef);
|
||
|
||
for (int j = 0; j < CFArrayGetCount(runs); j++)
|
||
{
|
||
CTRunRef runRef = CFArrayGetValueAtIndex(runs, j);
|
||
CGFloat runAscent;
|
||
CGFloat runDescent;
|
||
CGRect runRect;
|
||
|
||
runRect.size.width = CTRunGetTypographicBounds(runRef, CFRangeMake(0,0), &runAscent, &runDescent, NULL);
|
||
runRect = CGRectMake(lineOrigin.x + CTLineGetOffsetForStringIndex(lineRef, CTRunGetStringRange(runRef).location, NULL),
|
||
lineOrigin.y,
|
||
runRect.size.width,
|
||
runAscent + runDescent);
|
||
|
||
NSDictionary * attributes = (__bridge NSDictionary *)CTRunGetAttributes(runRef);
|
||
UMComMutiTextRun *mutiTextRun = [attributes objectForKey:UMComMutiTextRunAttributedName];
|
||
if (mutiTextRun.drawSelf)
|
||
{
|
||
CGFloat runAscent,runDescent;
|
||
CGFloat runWidth = CTRunGetTypographicBounds(runRef, CFRangeMake(0,0), &runAscent, &runDescent, NULL);
|
||
CGFloat runHeight = (lineAscent + lineDescent );
|
||
CGFloat runPointX = runRect.origin.x + lineOrigin.x;
|
||
CGFloat runPointY = lineOrigin.y;
|
||
|
||
CGRect runRectDraw = CGRectMake(runPointX, runPointY, runWidth, runHeight);
|
||
|
||
[mutiTextRun drawRunWithRect:runRectDraw];
|
||
|
||
[self.runRectDictionary setObject:mutiTextRun forKey:[NSValue valueWithCGRect:runRectDraw]];
|
||
}
|
||
else
|
||
{
|
||
if (mutiTextRun)
|
||
{
|
||
[self.runRectDictionary setObject:mutiTextRun forKey:[NSValue valueWithCGRect:runRect]];
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
- (void)tapInCerrentView:(UITapGestureRecognizer *)tap
|
||
{
|
||
CGPoint location = [tap locationInView:self];
|
||
CGPoint runLocation = CGPointMake(location.x-self.pointOffset.x, self.frame.size.height - location.y+self.pointOffset.y+2);
|
||
|
||
__weak UMComMutiStyleTextView *weakSelf = self;
|
||
if (self.clickOnlinkText) {
|
||
if (self.runRectDictionary.count > 0) {
|
||
BOOL isInclude = NO;
|
||
for (NSValue *key in [self.runRectDictionary allKeys]) {
|
||
id object = [self.runRectDictionary objectForKey:key];
|
||
CGRect rect = [((NSValue *)key) CGRectValue];
|
||
if(CGRectContainsPoint(rect, runLocation))
|
||
{
|
||
isInclude = YES;
|
||
weakSelf.touchRun = (UMComMutiTextRun *)object;
|
||
if ([object isKindOfClass:[UMComMutiTextRunURL class]]) {
|
||
if (![[weakSelf.touchRun.text lowercaseString] hasPrefix:@"http"]) {
|
||
weakSelf.touchRun.text = [NSString stringWithFormat:@"http://%@",weakSelf.touchRun.text];
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
if (isInclude == YES) {
|
||
weakSelf.clickOnlinkText(self,weakSelf.touchRun);
|
||
}else{
|
||
weakSelf.clickOnlinkText(self,nil);
|
||
}
|
||
}else{
|
||
weakSelf.clickOnlinkText(self,nil);
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
|
||
|
||
//#pragma mark -
|
||
//
|
||
//+ (NSMutableAttributedString *)createAttributedStringWithText:(NSString *)text font:(UIFont *)font lineSpace:(CGFloat)lineSpace
|
||
//{
|
||
// NSMutableAttributedString *attString = [[NSMutableAttributedString alloc] initWithString:text];
|
||
// //设置字体
|
||
// CTFontRef fontRef = CTFontCreateFromUIFont(font);
|
||
// [attString addAttribute:(NSString*)kCTFontAttributeName value:(__bridge id)fontRef range:NSMakeRange(0,attString.length)];
|
||
// CFRelease(fontRef);
|
||
//
|
||
// //添加换行模式
|
||
// CTParagraphStyleSetting lineBreakMode;
|
||
// CTLineBreakMode lineBreak = kCTLineBreakByWordWrapping;
|
||
// lineBreakMode.spec = kCTParagraphStyleSpecifierLineBreakMode;
|
||
// lineBreakMode.value = &lineBreak;
|
||
// lineBreakMode.valueSize = sizeof(lineBreak);
|
||
//
|
||
// //行距
|
||
// CTParagraphStyleSetting lineSpaceStyle;
|
||
// lineSpaceStyle.spec = kCTParagraphStyleSpecifierLineSpacing;
|
||
// lineSpaceStyle.valueSize = sizeof(lineSpace);
|
||
// lineSpaceStyle.value =&lineSpace;
|
||
//
|
||
// CTParagraphStyleSetting settings[] = {lineSpaceStyle,lineBreakMode};
|
||
// CTParagraphStyleRef style = CTParagraphStyleCreate(settings, sizeof(settings)/sizeof(settings[0]));
|
||
// NSMutableDictionary *attributes = [NSMutableDictionary dictionaryWithObject:(__bridge id)style forKey:(id)kCTParagraphStyleAttributeName ];
|
||
// CFRelease(style);
|
||
//
|
||
// [attString addAttributes:attributes range:NSMakeRange(0, [attString length])];
|
||
////
|
||
////
|
||
//// [attString addAttribute:(NSString*)kCTForegroundColorAttributeName value:[UIColor redColor] range:NSMakeRange(0,attString.length)];
|
||
// return attString;
|
||
//}
|
||
|
||
//+ (NSArray *)createTextRunsWithAttString:(NSMutableAttributedString *)attString
|
||
// runType:(UMComMutiTextRunTypeList)type
|
||
// font:(UIFont *)font
|
||
// checkWords:(NSArray *)checkWords
|
||
//{
|
||
// NSMutableArray *array = [[NSMutableArray alloc] init];
|
||
// UIColor *blueColor = UMComColorWithHexString(FontColorBlue];
|
||
// if (type == UMComMutiTextRunFeedContentType || type == UMComMutiTextRunCommentType || type == UMComMutiTextRunLikeType || type == UMComMutiTextRunNoneType)
|
||
// {
|
||
// NSArray *subRunArray = [UMComMutiTextRunURL runsWithAttributedString:attString font:font textColor:blueColor];
|
||
// if (subRunArray.count > 0) {
|
||
// [array addObjectsFromArray:subRunArray];
|
||
// }
|
||
// subRunArray = [UMComMutiTextRunTopic runsWithAttributedString:attString font:font textColor:blueColor checkWords:checkWords];
|
||
// if (subRunArray.count > 0) {
|
||
// [array addObjectsFromArray:subRunArray];
|
||
// }
|
||
// subRunArray = [UMComMutiTextRunClickUser runsWithAttributedString:attString font:font textColor:blueColor checkWords:checkWords];
|
||
// if (subRunArray.count > 0) {
|
||
// [array addObjectsFromArray:subRunArray];
|
||
// }
|
||
// }
|
||
// return array;
|
||
//}
|
||
|
||
|
||
//- (NSArray *)runsWithattributString:(NSMutableAttributedString *)attribuString
|
||
// Font:(UIFont *)font
|
||
// lineSpace:(CGFloat )lineSpace
|
||
// color:(UIColor *)color
|
||
// checkWords:(NSArray *)checkWords
|
||
//{
|
||
// return nil;
|
||
//}
|
||
|
||
//+ (UMComMutiStyleTextView *)rectDictionaryWithSize:(CGSize)size
|
||
// font:(UIFont *)font
|
||
// attString:(NSString *)string
|
||
// lineSpace:(CGFloat )lineSpace
|
||
// runType:(UMComMutiTextRunTypeList)runType
|
||
// checkWords:(NSMutableArray *)checkWords
|
||
//{
|
||
// UMComMutiStyleTextView * styleTextView = [[UMComMutiStyleTextView alloc] init];
|
||
// styleTextView.checkWords = checkWords;
|
||
// styleTextView.font = font;
|
||
// styleTextView.lineSpace = lineSpace;
|
||
// [styleTextView rectDictionaryWithSize:size font:font attString:string lineSpace:lineSpace];
|
||
// return styleTextView;
|
||
//}
|
||
|
||
//- (void)rectDictionaryWithSize:(CGSize)size font:(UIFont *)font attString:(NSString *)string lineSpace:(CGFloat )lineSpace
|
||
//{
|
||
// if (!string || string.length == 0) {
|
||
// return;
|
||
// }
|
||
// CGFloat shortestLineWith = 0;
|
||
// int lineCount = 0;
|
||
// NSMutableAttributedString *attString = [[self class] createAttributedStringWithText:string font:font lineSpace:lineSpace];
|
||
// NSDictionary *dic = [attString attributesAtIndex:0 effectiveRange:nil];
|
||
// CTParagraphStyleRef paragraphStyle = (__bridge CTParagraphStyleRef)[dic objectForKey:(id)kCTParagraphStyleAttributeName];
|
||
// CGFloat linespace = 0;
|
||
//
|
||
// CTParagraphStyleGetValueForSpecifier(paragraphStyle, kCTParagraphStyleSpecifierLineSpacing, sizeof(linespace), &linespace);
|
||
//
|
||
// CGFloat height = 0;
|
||
// CGFloat width = 0;
|
||
// CFIndex lineIndex = 0;
|
||
//
|
||
// CGMutablePathRef pathRef = CGPathCreateMutable();
|
||
// CGPathAddRect(pathRef, NULL, CGRectMake(0, 0, size.width, size.height));
|
||
//
|
||
// NSArray *runs = [[self class] createTextRunsWithAttString:attString runType:0 font:font checkWords:self.checkWords];
|
||
// self.runs = runs;
|
||
//
|
||
// CFAttributedStringRef attStringRef = (__bridge CFAttributedStringRef)attString;
|
||
// CTFramesetterRef setterRef = CTFramesetterCreateWithAttributedString(attStringRef);
|
||
// self.framesetterRef = (__bridge_transfer id)(CFRetain(setterRef));
|
||
// CTFrameRef frameRef = CTFramesetterCreateFrame(setterRef, CFRangeMake(0, 0), pathRef, nil);
|
||
// CFArrayRef lines = CTFrameGetLines(frameRef);
|
||
//
|
||
// lineIndex = CFArrayGetCount(lines);
|
||
// lineCount = (int)lineIndex;
|
||
//
|
||
// CTLineRef lineRef;
|
||
// if (lineIndex > 1)
|
||
// {
|
||
// for (int i = 0; i <lineIndex ; i++)
|
||
// {
|
||
// lineRef= CFArrayGetValueAtIndex(lines, i);
|
||
// if (i == lineIndex - 1) {
|
||
// CGRect rect = CTLineGetBoundsWithOptions(lineRef,kCTLineBoundsExcludeTypographicShifts);
|
||
// shortestLineWith = rect.size.width;
|
||
// }
|
||
// CGFloat lineAscent;
|
||
// CGFloat lineDescent;
|
||
// CGFloat lineLeading;
|
||
// CTLineGetTypographicBounds(lineRef, &lineAscent, &lineDescent, &lineLeading);
|
||
//
|
||
// if (i == lineIndex - 1)
|
||
// {
|
||
// height += (lineAscent + lineDescent +linespace);
|
||
// }
|
||
// else
|
||
// {
|
||
// height += (lineAscent + lineDescent + linespace);
|
||
// }
|
||
// }
|
||
// width = size.width;
|
||
// }
|
||
// else
|
||
// {
|
||
// lineRef= CFArrayGetValueAtIndex(lines, 0);
|
||
// CGRect rect = CTLineGetBoundsWithOptions(lineRef,kCTLineBoundsExcludeTypographicShifts);
|
||
// shortestLineWith = rect.size.width;
|
||
//// self.lineHeight = rect.size.height + linespace;
|
||
// width = rect.size.width;
|
||
// CGFloat lineAscent;
|
||
// CGFloat lineDescent;
|
||
// CGFloat lineLeading;
|
||
// CTLineGetTypographicBounds(lineRef, &lineAscent, &lineDescent, &lineLeading);
|
||
//
|
||
// height += (lineAscent + lineDescent + lineLeading + linespace);
|
||
// height = height;
|
||
// }
|
||
//
|
||
//// self.totalHeight = height + lineSpace/2;
|
||
// self.attributedText = attString;
|
||
// CGPathRelease(pathRef);
|
||
// CFRelease(setterRef);
|
||
// CFRelease(frameRef);
|
||
//}
|
||
|
||
@end
|
||
|
||
|
||
|