1、添加手势
- (void)configSettings{
//添加手势
UILongPressGestureRecognizer *longPressGesture = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longPress:)];
longPressGesture.minimumPressDuration = 0.01;
longPressGesture.delegate = self;
[self addGestureRecognizer:longPressGesture];
}
2、绘制
- (void)drawRect:(CGRect)rect{
[super drawRect:rect];
[self drawRectWithCheckClick];
}
#pragma mark - 绘制
- (void)drawRectWithCheckClick{
// 1.创建需要绘制的文字
NSMutableAttributedString *attributed = [[NSMutableAttributedString alloc] initWithString:self.text];
// 2.1设置行距等样式
[[self class] addGlobalAttributeWithContent:attributed font:self.font];
// 2.2识别特定字符串并改变其颜色
[self recognizeSpecialStringWithAttributed:attributed];
//2.3加一个点击改变字符串颜色的效果
if (self.pressRange.location != 0 && self.pressRange.length != 0){
[attributed addAttribute:NSForegroundColorAttributeName value:[UIColor yellowColor] range:self.pressRange];
}
self.textHeight = [[self class] textHeightWithText:self.text width:CGRectGetWidth(self.bounds) font:self.font type:self.drawType];
// 3.创建绘制区域,path的高度对绘制有直接影响,如果高度不够,则计算出来的CTLine的数量会少一行或者少多行
CGMutablePathRef path = CGPathCreateMutable();
CGPathAddRect(path, NULL, CGRectMake(0, 0, CGRectGetWidth(self.bounds), self.textHeight*2));
// 4.根据NSAttributedString生成CTFramesetterRef
CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)attributed);
CTFrameRef ctFrame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, attributed.length), path, NULL);
self.ctFrame = CFRetain(ctFrame);
// 获取上下文
CGContextRef contextRef = UIGraphicsGetCurrentContext();
// 转换坐标系
CGContextSetTextMatrix(contextRef, CGAffineTransformIdentity);
CGContextTranslateCTM(contextRef, 0, self.textHeight); // 此处用计算出来的高度
CGContextScaleCTM(contextRef, 1.0, -1.0);
// 一行一行绘制
CFArrayRef lines = CTFrameGetLines(ctFrame);
CFIndex lineCount = CFArrayGetCount(lines);
CGPoint lineOrigins[lineCount];
// 把ctFrame里每一行的初始坐标写到数组里,注意CoreText的坐标是左下角为原点
CTFrameGetLineOrigins(ctFrame, CFRangeMake(0, 0), lineOrigins);
CGFloat frameY = 0;
for (CFIndex i = 0; i < lineCount; i++){
// 遍历每一行CTLine
CTLineRef line = CFArrayGetValueAtIndex(lines, i);
CGFloat lineAscent;
CGFloat lineDescent;
CGFloat lineLeading; // 行距
// 该函数除了会设置好ascent,descent,leading之外,还会返回这行的宽度
CTLineGetTypographicBounds(line, &lineAscent, &lineDescent, &lineLeading);
CGPoint lineOrigin = lineOrigins[i];
// 微调Y值,需要注意的是CoreText的Y值是在baseLine处,而不是下方的descent。
CGFloat lineHeight = self.font.pointSize * kPerLineRatio;
frameY = self.textHeight - (i + 1)*lineHeight - self.font.descender;
lineOrigin.y = frameY;
// 调整坐标
CGContextSetTextPosition(contextRef, lineOrigin.x, lineOrigin.y);
CTLineDraw(line, contextRef);
}
CFRelease(path);
CFRelease(framesetter);
CFRelease(ctFrame);
}
3、给字符串添加属性
#pragma mark - 工具方法
#pragma mark 给字符串添加全局属性,比如行距,字体大小,默认颜色
+ (void)addGlobalAttributeWithContent:(NSMutableAttributedString *)attributeString font:(UIFont *)aFont{
CGFloat lineLeading = kGlobalLineLeading; // 行间距
//设置部分颜色
[attributeString addAttribute:(NSString *)kCTForegroundColorAttributeName value:(id)[UIColor greenColor].CGColor range:NSMakeRange(10, 10)];
//设置文字
CTFontRef fontRef = CTFontCreateWithName((CFStringRef)@"ArialMT", aFont.pointSize, NULL);
[attributeString addAttribute:(NSString *)kCTFontAttributeName value:(__bridge id)fontRef range:NSMakeRange(0, attributeString.length)];
CFRelease(fontRef);
// 设置行距等样式
CGFloat lineSpace = lineLeading; // 行距一般取决于这个值
CGFloat lineSpaceMax = 20;
CGFloat lineSpaceMin = 2;
const CFIndex kNumberOfSettings = 3;
// 结构体数组
CTParagraphStyleSetting theSettings[kNumberOfSettings] = {
{kCTParagraphStyleSpecifierLineSpacingAdjustment,sizeof(CGFloat),&lineSpace},
{kCTParagraphStyleSpecifierMaximumLineSpacing,sizeof(CGFloat),&lineSpaceMax},
{kCTParagraphStyleSpecifierMinimumLineSpacing,sizeof(CGFloat),&lineSpaceMin}
};
CTParagraphStyleRef theParagraphRef = CTParagraphStyleCreate(theSettings, kNumberOfSettings);
[attributeString addAttribute:(id)kCTParagraphStyleAttributeName value:(__bridge id)theParagraphRef range:NSMakeRange(0, attributeString.length)];
// 内存管理
CFRelease(theParagraphRef);
}
4、识别相关字符串
#pragma mark - 识别特定字符串并改其颜色,返回识别到的字符串所在的range
- (NSMutableArray *)recognizeSpecialStringWithAttributed:(NSMutableAttributedString *)attributed{
NSMutableArray *rangeArray = [NSMutableArray array];
// 识别@人名
NSRegularExpression *atRegular = [NSRegularExpression regularExpressionWithPattern:kAtRegularExpression options:NSRegularExpressionCaseInsensitive error:nil];
NSArray *atResults = [atRegular matchesInString:self.text options:NSMatchingWithTransparentBounds range:NSMakeRange(0, self.text.length)];
for (NSTextCheckingResult *checkResult in atResults){
if (attributed){
[attributed addAttribute:NSForegroundColorAttributeName value:[UIColor redColor] range:NSMakeRange(checkResult.range.location, checkResult.range.length -1)];
}
[rangeArray addObject:[NSValue valueWithRange:checkResult.range]];
}
// 识别连续的数字
NSRegularExpression *numberRegular = [NSRegularExpression regularExpressionWithPattern:kNumberRegularExpression options:NSRegularExpressionCaseInsensitive|NSRegularExpressionUseUnixLineSeparators error:nil];
NSArray *numberResults = [numberRegular matchesInString:self.text options:NSMatchingWithTransparentBounds range:NSMakeRange(0, self.text.length)];
for (NSTextCheckingResult *checkResult in numberResults){
if (attributed){
[attributed addAttribute:NSForegroundColorAttributeName value:[UIColor blueColor] range:NSMakeRange(checkResult.range.location, checkResult.range.length-1)];
}
[rangeArray addObject:[NSValue valueWithRange:NSMakeRange(checkResult.range.location, checkResult.range.length -1)]];
}
return rangeArray;
}
5、固定行高
/**
* 固定行高
* 高度 = 每行的固定高度 * 行数
*/
+ (CGFloat)textHeightWithText3:(NSString *)aText width:(CGFloat)aWidth font:(UIFont *)aFont{
NSMutableAttributedString *content = [[NSMutableAttributedString alloc] initWithString:aText];
// 给字符串设置字体行距等样式
[self addGlobalAttributeWithContent:content font:aFont];
CTFramesetterRef framesetterRef = CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef)content);
// 粗略的高度,该高度不准,仅供参考
CGSize suggestSize = CTFramesetterSuggestFrameSizeWithConstraints(framesetterRef, CFRangeMake(0, content.length), NULL, CGSizeMake(aWidth, MAXFLOAT), NULL);
CGMutablePathRef pathRef = CGPathCreateMutable();
CGPathAddRect(pathRef, NULL, CGRectMake(0, 0, aWidth, suggestSize.height));
CTFrameRef frameRef = CTFramesetterCreateFrame(framesetterRef, CFRangeMake(0, content.length), pathRef, NULL);
CFArrayRef lines = CTFrameGetLines(frameRef);
CFIndex lineCount = CFArrayGetCount(lines);
// 总高度 = 行数*每行的高度,其中每行的高度为指定的值,不同字体大小不一样
CGFloat accurateHeight = lineCount * (aFont.pointSize * kPerLineRatio);
CGFloat height = accurateHeight;
CFRelease(pathRef);
CFRelease(frameRef);
return height;
}
6、手势相关
#pragma mark - 手势识别相关
- (void)longPress:(UIGestureRecognizer *)gesture{
// 改变字符串的颜色并进行重绘
if (gesture.state == UIGestureRecognizerStateBegan){
if (self.pressRange.length != 0||self.pressRange.location != 0) {
[self setNeedsDisplay];
}
}else if(gesture.state == UIGestureRecognizerStateEnded){
if (self.pressRange.location != 0 && self.pressRange.length != 0){
NSString *clickStr = [self.text substringWithRange:self.pressRange];
NSLog(@"点击了 %@",clickStr);
self.pressRange = NSMakeRange(0, 0);
[self setNeedsDisplay];
}
}
}
#pragma mark - UIGestureRecognizerDelegate
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer{
// 点击处在特定字符串内才进行识别
BOOL gestureShouldBegin = NO;
CGPoint location = [gestureRecognizer locationInView:self];
//单行高度
CGFloat lineHeight = self.font.pointSize * kPerLineRatio;
//点击行数
int lineIndex = location.y/lineHeight;
// 把点击的坐标转换为CoreText坐标系下
CGPoint clickPoint = CGPointMake(location.x, self.height - location.y);
CFArrayRef lines = CTFrameGetLines(self.ctFrame);
if (lineIndex < CFArrayGetCount(lines)){
CTLineRef clickLine = CFArrayGetValueAtIndex(lines, lineIndex);
// 点击处的字符位于总字符串的index
CFIndex strIndex = CTLineGetStringIndexForPosition(clickLine, clickPoint);
NSMutableAttributedString *mutableAttributed = [[NSMutableAttributedString alloc] initWithString:self.text];
NSArray *checkResults = [self recognizeSpecialStringWithAttributed:mutableAttributed];
for (NSValue *value in checkResults){
NSRange range = [value rangeValue];
if (strIndex >= range.location && strIndex <= range.location + range.length){
self.pressRange = range;
gestureShouldBegin = YES;
}
}
}
return gestureShouldBegin;
}
// 该方法可实现也可不实现,取决于应用场景
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRequireFailureOfGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
if ([otherGestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]])
{
return YES; // 避免应用在UITableViewCell上时,挡住拖动tableView的手势
}
return NO;
}
作者:xiaoxiaobukuang 发表于2016/10/11 11:14:33 原文链接
阅读:22 评论:0 查看评论