IOS CoreText系列四:图文混排之点击事件
2016-11-18 22:18
387 查看
主要思路:基于-(void)touchesBegan:(NSSet<UITouch *> )touches withEvent:(UIEvent )event这个方法拿到当前点击到的点,然后通过坐标判断这个点是否在某段文字上,如果在则触发对应事件。
1、首先需要执行touchesBegan这个方法来做相应的判断
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint location = [self systemPointFromScreenPoint:[touch locationInView:self]];
//判断点击的是图片还是文字
if ([self checkIsClickOnImgWithPoint:location]) {
return;
}
[self clickOnStringWithPoint:location];
}
2、点击的是图片做相应的处理,图片的位置我们在图文混排的时候会获取到,这边直接拿过来用,如果图片有很多张的话,需要搞个循环来做判断。
#pragma mark 点击图片
- (BOOL)checkIsClickOnImgWithPoint:(CGPoint)location
{
if ([self isFrame:_imgFrm containsPoint:location]) {
NSLog(@"你点击到了图片");
return YES;
}
return NO;
}
- (BOOL)isFrame:(CGRect)frame containsPoint:(CGPoint)point
{
return CGRectContainsPoint(frame, point);
}3、点击的是字符串,步骤如下:
/*
点击文字判断的大致步骤:
1.根据Frame拿到所有Line
2.计算每个Line中在全文的range
3.计算每个字对应line原点的X值
4.比对对应line的origin求得字对应起点坐标
5.求得下一个字的横坐标和上一行的origin,结合起点坐标得出字的坐标范围
6.屏幕坐标与drawRect坐标转换,判断是否在范围内
*/
完整代码如下:
.h文件
#import <UIKit/UIKit.h>
@interface HSCoreTextView : UIView
@end
.m文件
#import "HSCoreTextView.h"
#import <CoreText/CoreText.h>
@interface HSCoreTextView ()
@property (nonatomic,assign) CGRect imgFrm;
@property (nonatomic,assign) CTFrameRef ctFrm;
@property (nonatomic,assign) NSInteger strLength;
@end
@implementation HSCoreTextView
//图片回调函数
static CGFloat ascentCallBacks(void *ref)
{
//__bridge既是C的结构体转换成OC对象时需要的一个修饰词
return [[(__bridge NSDictionary *)ref valueForKey:@"height"] floatValue];
}
static CGFloat descentCallBacks(void *ref)
{
return 0;
}
static CGFloat widthCallBacks(void *ref)
{
return [[(__bridge NSDictionary *)ref valueForKey:@"width"] floatValue];
}
- (void)drawRect:(CGRect)rect
{
[super drawRect:rect];
//1.绘制上下文
//1.1获取绘制上下文
CGContextRef context = UIGraphicsGetCurrentContext();
//1.2.coreText 起初是为OSX设计的,而OSX得坐标原点是左下角,y轴正方向朝上。iOS中坐标原点是左上角,y轴正方向向下。若不进行坐标转换,则文字从下开始,还是倒着的,因此需要设置以下属性
////设置字形的变换矩阵为不做图形变换
CGContextSetTextMatrix(context, CGAffineTransformIdentity);
//平移方法,将画布向上平移一个屏幕高
CGContextTranslateCTM(context, 0, self.bounds.size.height);
//缩放方法,x轴缩放系数为1,则不变,y轴缩放系数为-1,则相当于以x轴为轴旋转180度
CGContextScaleCTM(context, 1.0, -1.0);
//2.设置图片回调函数
//2.1创建一个回调结构体,设置相关参数
CTRunDelegateCallbacks callBacks;
//memset将已开辟内存空间 callbacks 的首 n 个字节的值设为值 0, 相当于对CTRunDelegateCallbacks内存空间初始化
memset(&callBacks, 0, sizeof(CTRunDelegateCallbacks));
//2.2设置回调版本,默认这个
callBacks.version = kCTRunDelegateVersion1;
//2.3设置图片顶部距离基线的距离
callBacks.getAscent = ascentCallBacks;
//2.4设置图片底部距离基线的距离
callBacks.getDescent = descentCallBacks;
//2.5设置图片宽度
callBacks.getWidth = widthCallBacks;
//2.6创建一个代理
NSDictionary *dicPic = @{@"height":@"60",@"width":@"60"};
CTRunDelegateRef delegate = CTRunDelegateCreate(&callBacks, (__bridge void * _Nullable)(dicPic));
//3.插入图片
//创建空白字符
unichar placeHolder = 0xFFFC;
NSString *placeHolderStr = [NSString stringWithCharacters:&placeHolder length:1];
NSMutableAttributedString *placeHolderMabString = [[NSMutableAttributedString alloc] initWithString:placeHolderStr];
//给字符串中的范围中字符串设置代理
CFAttributedStringSetAttribute((CFMutableAttributedStringRef)placeHolderMabString, CFRangeMake(0, 1), kCTRunDelegateAttributeName, delegate);
//4.设置要显示的文字
NSMutableAttributedString *mabString = [[NSMutableAttributedString alloc] initWithString:@"\n这里在测试图文混排,\n我是富文本"];
[mabString insertAttributedString:placeHolderMabString atIndex:12];
//5.绘制文本
//5.1.创建CTFramesetterRef
CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)mabString);
//5.2.创建路径
CGMutablePathRef path = CGPathCreateMutable();
CGPathAddRect(path, NULL, self.bounds);
//5.3.创建CTFrame
NSInteger length = mabString.length;
self.strLength = length;
CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, length), path, NULL);
self.ctFrm = frame;
CTFrameDraw(frame, context);
//6.添加图片并绘制
UIImage *image = [UIImage imageNamed:@"icon-60"];
CGRect imgFrm = [self calculateImageRectWithFrame:frame];
self.imgFrm = imgFrm;
CGContextDrawImage(context, imgFrm, image.CGImage);
//7.释放
CFRelease(delegate);
// CFRelease(frame);
CFRelease(path);
CFRelease(framesetter);
}
#pragma mark 计算图片Frame
- (CGRect)calculateImageRectWithFrame:(CTFrameRef)frame
{
//根据frame获取需要绘制的线的数组
NSArray *arrLines = (NSArray *)CTFrameGetLines(frame);
NSInteger count = arrLines.count;
CGPoint points[count];
//获取起始点位置
CTFrameGetLineOrigins(frame, CFRangeMake(0, 0), points);
for (int i = 0; i < count; i ++) {
CTLineRef line = (__bridge CTLineRef)(arrLines[i]);
//CTRun 或者叫做 Glyph Run,是一组共享想相同attributes(属性)的字形的集合体
NSArray *arrGlyphRun = (NSArray *)CTLineGetGlyphRuns(line);
for (int j = 0; j < arrGlyphRun.count; j ++) {
CTRunRef run = (__bridge CTRunRef)(arrGlyphRun[j]);
//获取CTRun的属性
NSDictionary *attributes = (NSDictionary *)CTRunGetAttributes(run);
CTRunDelegateRef delegate = (__bridge CTRunDelegateRef)[attributes valueForKey:(id)kCTRunDelegateAttributeName];
if (delegate == nil) {
continue;
}
NSDictionary *dic = CTRunDelegateGetRefCon(delegate);
if (![dic isKindOfClass:[NSDictionary class]]) {
continue;
}
//获取一个起点
CGPoint point = points[i];
//获取上下距
CGFloat ascent,desecent;
//创建一个Frame
CGRect boundsRun;
boundsRun.size.width = CTRunGetTypographicBounds(run, CFRangeMake(0, 0), &ascent, &desecent, NULL);
boundsRun.size.height = ascent + desecent;
//获取偏移量
CGFloat xOffset = CTLineGetOffsetForStringIndex(line, CTRunGetStringRange(run).location, NULL);
boundsRun.origin.x = point.x + xOffset;
boundsRun.origin.y = point.y - desecent;
//获取绘制路径
CGPathRef path = CTFrameGetPath(frame);
//获取剪裁区域边框
CGRect colRect = CGPathGetBoundingBox(path);
CGRect imageBounds = CGRectOffset(boundsRun, colRect.origin.x, colRect.origin.y);
return imageBounds;
}
}
return CGRectZero;
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint location = [self systemPointFromScreenPoint:[touch locationInView:self]];
//判断点击的是图片还是文字
if ([self checkIsClickOnImgWithPoint:location]) {
return;
}
[self clickOnStringWithPoint:location];
}
#pragma mark 转化成屏幕坐标
- (CGPoint)systemPointFromScreenPoint:(CGPoint)origin
{
return CGPointMake(origin.x, self.bounds.size.height - origin.y);
}
#pragma mark 点击图片
- (BOOL)checkIsClickOnImgWithPoint:(CGPoint)location
{
if ([self isFrame:_imgFrm containsPoint:location]) {
NSLog(@"你点击到了图片");
return YES;
}
return NO;
}
- (BOOL)isFrame:(CGRect)frame containsPoint:(CGPoint)point
{
return CGRectContainsPoint(frame, point);
}
/*
点击文字判断的大致步骤:
1.根据Frame拿到所有Line
2.计算每个Line中在全文的range
3.计算每个字对应line原点的X值
4.比对对应line的origin求得字对应起点坐标
5.求得下一个字的横坐标和上一行的origin,结合起点坐标得出字的坐标范围
6.屏幕坐标与drawRect坐标转换,判断是否在范围内
*/
#pragma mark 点击字符串 - (void)clickOnStringWithPoint:(CGPoint)point { //获取所有CTLine NSArray *lines = (NSArray *)CTFrameGetLines(self.ctFrm); //初始化范围数组 CFRange ranges[lines.count]; //初始化原点数组 CGPoint origins[lines.count]; //获取所有CTLine的原点 CTFrameGetLineOrigins(self.ctFrm, CFRangeMake(0, 0), origins); //获取每个CTLine中包含的富文本在整串富文本中的范围。将所有CTLine中字符串的范围保存下来放入数组中。 for (int i = 0; i < lines.count; i ++) { CTLineRef line = (__bridge CTLineRef)(lines[i]); CFRange range = CTLineGetStringRange(line); ranges[i] = range; } for (int i = 0;i < self.strLength; i ++) { long maxLoc; int lineNum; for (int j = 0; j < lines.count; j ++) { CFRange range = ranges[j]; maxLoc = range.location + range.length - 1; if (i <= maxLoc) { lineNum = j; break; } } CTLineRef line = (__bridge CTLineRef)(lines[lineNum]); CGPoint origin = origins[lineNum]; CGRect ctRunFrame = [self frameForCTRunWithIndex:i ctLine:line origin:origin]; if ([self isFrame:ctRunFrame containsPoint:point]) { NSLog(@"您点击到了第 %d 个字符,位于第 %d 行,然而他没有响应事件。",i,lineNum + 1); return; } } NSLog(@"您没有点击到文字"); } /** 字符Frame计算 @param index 索引 @param line 索引字符所在CTLine @param origin line的起点 @return 返回Frame */ - (CGRect)frameForCTRunWithIndex:(NSInteger)index ctLine:(CTLineRef)line origin:(CGPoint)origin { //获取字符起点相对于CTLine的原点的偏移量 CGFloat offsetX = CTLineGetOffsetForStringIndex(line, index, NULL); //获取下一个字符的偏移量,两者之间即为字符X范围 CGFloat offsetX2 = CTLineGetOffsetForStringIndex(line, index + 1, NULL); offsetX += origin.x; offsetX2 += origin.x; CGFloat offsetY = origin.y; CGFloat lineAscent,lineDescent; //获取当前点击的CTRun NSArray *runs = (NSArray *)CTLineGetGlyphRuns(line); CTRunRef runCurrent; for (int k = 0; k < runs.count; k ++) { CTRunRef run = (__bridge CTRunRef)(runs[k]); CFRange range = CTRunGetStringRange(run); NSRange rangeOC = NSMakeRange(range.location, range.length); if ([self isIndex:index inRange:rangeOC]) { runCurrent = run; break; } } //获得对应CTRun的尺寸信息 CTRunGetTypographicBounds(runCurrent, CFRangeMake(0, 0), &lineAscent, &lineDescent, NULL); //计算当前点击的CTRun高度 CGFloat height = lineAscent + lineDescent; return CGRectMake(offsetX, offsetY, offsetX2 - offsetX, height); } /** 范围检测 @param index 索引 @param range 范围 @return 范围内返回yes,否则返回no */ - (BOOL)isIndex:(NSInteger)index inRange:(NSRange)range { if ((index <= (range.location + range.length - 1)) && (index >= range.location)) { return YES; } return NO; }
- (void)dealloc
{
CFRelease(_ctFrm);
}
@end
1、首先需要执行touchesBegan这个方法来做相应的判断
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint location = [self systemPointFromScreenPoint:[touch locationInView:self]];
//判断点击的是图片还是文字
if ([self checkIsClickOnImgWithPoint:location]) {
return;
}
[self clickOnStringWithPoint:location];
}
2、点击的是图片做相应的处理,图片的位置我们在图文混排的时候会获取到,这边直接拿过来用,如果图片有很多张的话,需要搞个循环来做判断。
#pragma mark 点击图片
- (BOOL)checkIsClickOnImgWithPoint:(CGPoint)location
{
if ([self isFrame:_imgFrm containsPoint:location]) {
NSLog(@"你点击到了图片");
return YES;
}
return NO;
}
- (BOOL)isFrame:(CGRect)frame containsPoint:(CGPoint)point
{
return CGRectContainsPoint(frame, point);
}3、点击的是字符串,步骤如下:
/*
点击文字判断的大致步骤:
1.根据Frame拿到所有Line
2.计算每个Line中在全文的range
3.计算每个字对应line原点的X值
4.比对对应line的origin求得字对应起点坐标
5.求得下一个字的横坐标和上一行的origin,结合起点坐标得出字的坐标范围
6.屏幕坐标与drawRect坐标转换,判断是否在范围内
*/
#pragma mark 点击字符串 - (void)clickOnStringWithPoint:(CGPoint)point { //获取所有CTLine NSArray *lines = (NSArray *)CTFrameGetLines(self.ctFrm); //初始化范围数组 CFRange ranges[lines.count]; //初始化原点数组 CGPoint origins[lines.count]; //获取所有CTLine的原点 CTFrameGetLineOrigins(self.ctFrm, CFRangeMake(0, 0), origins); //获取每个CTLine中包含的富文本在整串富文本中的范围。将所有CTLine中字符串的范围保存下来放入数组中。 for (int i = 0; i < lines.count; i ++) { CTLineRef line = (__bridge CTLineRef)(lines[i]); CFRange range = CTLineGetStringRange(line); ranges[i] = range; } for (int i = 0;i < self.strLength; i ++) { long maxLoc; int lineNum; for (int j = 0; j < lines.count; j ++) { CFRange range = ranges[j]; maxLoc = range.location + range.length - 1; if (i <= maxLoc) { lineNum = j; break; } } CTLineRef line = (__bridge CTLineRef)(lines[lineNum]); CGPoint origin = origins[lineNum]; CGRect ctRunFrame = [self frameForCTRunWithIndex:i ctLine:line origin:origin]; if ([self isFrame:ctRunFrame containsPoint:point]) { NSLog(@"您点击到了第 %d 个字符,位于第 %d 行,然而他没有响应事件。",i,lineNum + 1); return; } } NSLog(@"您没有点击到文字"); } /** 字符Frame计算 @param index 索引 @param line 索引字符所在CTLine @param origin line的起点 @return 返回Frame */ - (CGRect)frameForCTRunWithIndex:(NSInteger)index ctLine:(CTLineRef)line origin:(CGPoint)origin { //获取字符起点相对于CTLine的原点的偏移量 CGFloat offsetX = CTLineGetOffsetForStringIndex(line, index, NULL); //获取下一个字符的偏移量,两者之间即为字符X范围 CGFloat offsetX2 = CTLineGetOffsetForStringIndex(line, index + 1, NULL); offsetX += origin.x; offsetX2 += origin.x; CGFloat offsetY = origin.y; CGFloat lineAscent,lineDescent; //获取当前点击的CTRun NSArray *runs = (NSArray *)CTLineGetGlyphRuns(line); CTRunRef runCurrent; for (int k = 0; k < runs.count; k ++) { CTRunRef run = (__bridge CTRunRef)(runs[k]); CFRange range = CTRunGetStringRange(run); NSRange rangeOC = NSMakeRange(range.location, range.length); if ([self isIndex:index inRange:rangeOC]) { runCurrent = run; break; } } //获得对应CTRun的尺寸信息 CTRunGetTypographicBounds(runCurrent, CFRangeMake(0, 0), &lineAscent, &lineDescent, NULL); //计算当前点击的CTRun高度 CGFloat height = lineAscent + lineDescent; return CGRectMake(offsetX, offsetY, offsetX2 - offsetX, height); } /** 范围检测 @param index 索引 @param range 范围 @return 范围内返回yes,否则返回no */ - (BOOL)isIndex:(NSInteger)index inRange:(NSRange)range { if ((index <= (range.location + range.length - 1)) && (index >= range.location)) { return YES; } return NO; }
完整代码如下:
.h文件
#import <UIKit/UIKit.h>
@interface HSCoreTextView : UIView
@end
.m文件
#import "HSCoreTextView.h"
#import <CoreText/CoreText.h>
@interface HSCoreTextView ()
@property (nonatomic,assign) CGRect imgFrm;
@property (nonatomic,assign) CTFrameRef ctFrm;
@property (nonatomic,assign) NSInteger strLength;
@end
@implementation HSCoreTextView
//图片回调函数
static CGFloat ascentCallBacks(void *ref)
{
//__bridge既是C的结构体转换成OC对象时需要的一个修饰词
return [[(__bridge NSDictionary *)ref valueForKey:@"height"] floatValue];
}
static CGFloat descentCallBacks(void *ref)
{
return 0;
}
static CGFloat widthCallBacks(void *ref)
{
return [[(__bridge NSDictionary *)ref valueForKey:@"width"] floatValue];
}
- (void)drawRect:(CGRect)rect
{
[super drawRect:rect];
//1.绘制上下文
//1.1获取绘制上下文
CGContextRef context = UIGraphicsGetCurrentContext();
//1.2.coreText 起初是为OSX设计的,而OSX得坐标原点是左下角,y轴正方向朝上。iOS中坐标原点是左上角,y轴正方向向下。若不进行坐标转换,则文字从下开始,还是倒着的,因此需要设置以下属性
////设置字形的变换矩阵为不做图形变换
CGContextSetTextMatrix(context, CGAffineTransformIdentity);
//平移方法,将画布向上平移一个屏幕高
CGContextTranslateCTM(context, 0, self.bounds.size.height);
//缩放方法,x轴缩放系数为1,则不变,y轴缩放系数为-1,则相当于以x轴为轴旋转180度
CGContextScaleCTM(context, 1.0, -1.0);
//2.设置图片回调函数
//2.1创建一个回调结构体,设置相关参数
CTRunDelegateCallbacks callBacks;
//memset将已开辟内存空间 callbacks 的首 n 个字节的值设为值 0, 相当于对CTRunDelegateCallbacks内存空间初始化
memset(&callBacks, 0, sizeof(CTRunDelegateCallbacks));
//2.2设置回调版本,默认这个
callBacks.version = kCTRunDelegateVersion1;
//2.3设置图片顶部距离基线的距离
callBacks.getAscent = ascentCallBacks;
//2.4设置图片底部距离基线的距离
callBacks.getDescent = descentCallBacks;
//2.5设置图片宽度
callBacks.getWidth = widthCallBacks;
//2.6创建一个代理
NSDictionary *dicPic = @{@"height":@"60",@"width":@"60"};
CTRunDelegateRef delegate = CTRunDelegateCreate(&callBacks, (__bridge void * _Nullable)(dicPic));
//3.插入图片
//创建空白字符
unichar placeHolder = 0xFFFC;
NSString *placeHolderStr = [NSString stringWithCharacters:&placeHolder length:1];
NSMutableAttributedString *placeHolderMabString = [[NSMutableAttributedString alloc] initWithString:placeHolderStr];
//给字符串中的范围中字符串设置代理
CFAttributedStringSetAttribute((CFMutableAttributedStringRef)placeHolderMabString, CFRangeMake(0, 1), kCTRunDelegateAttributeName, delegate);
//4.设置要显示的文字
NSMutableAttributedString *mabString = [[NSMutableAttributedString alloc] initWithString:@"\n这里在测试图文混排,\n我是富文本"];
[mabString insertAttributedString:placeHolderMabString atIndex:12];
//5.绘制文本
//5.1.创建CTFramesetterRef
CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)mabString);
//5.2.创建路径
CGMutablePathRef path = CGPathCreateMutable();
CGPathAddRect(path, NULL, self.bounds);
//5.3.创建CTFrame
NSInteger length = mabString.length;
self.strLength = length;
CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, length), path, NULL);
self.ctFrm = frame;
CTFrameDraw(frame, context);
//6.添加图片并绘制
UIImage *image = [UIImage imageNamed:@"icon-60"];
CGRect imgFrm = [self calculateImageRectWithFrame:frame];
self.imgFrm = imgFrm;
CGContextDrawImage(context, imgFrm, image.CGImage);
//7.释放
CFRelease(delegate);
// CFRelease(frame);
CFRelease(path);
CFRelease(framesetter);
}
#pragma mark 计算图片Frame
- (CGRect)calculateImageRectWithFrame:(CTFrameRef)frame
{
//根据frame获取需要绘制的线的数组
NSArray *arrLines = (NSArray *)CTFrameGetLines(frame);
NSInteger count = arrLines.count;
CGPoint points[count];
//获取起始点位置
CTFrameGetLineOrigins(frame, CFRangeMake(0, 0), points);
for (int i = 0; i < count; i ++) {
CTLineRef line = (__bridge CTLineRef)(arrLines[i]);
//CTRun 或者叫做 Glyph Run,是一组共享想相同attributes(属性)的字形的集合体
NSArray *arrGlyphRun = (NSArray *)CTLineGetGlyphRuns(line);
for (int j = 0; j < arrGlyphRun.count; j ++) {
CTRunRef run = (__bridge CTRunRef)(arrGlyphRun[j]);
//获取CTRun的属性
NSDictionary *attributes = (NSDictionary *)CTRunGetAttributes(run);
CTRunDelegateRef delegate = (__bridge CTRunDelegateRef)[attributes valueForKey:(id)kCTRunDelegateAttributeName];
if (delegate == nil) {
continue;
}
NSDictionary *dic = CTRunDelegateGetRefCon(delegate);
if (![dic isKindOfClass:[NSDictionary class]]) {
continue;
}
//获取一个起点
CGPoint point = points[i];
//获取上下距
CGFloat ascent,desecent;
//创建一个Frame
CGRect boundsRun;
boundsRun.size.width = CTRunGetTypographicBounds(run, CFRangeMake(0, 0), &ascent, &desecent, NULL);
boundsRun.size.height = ascent + desecent;
//获取偏移量
CGFloat xOffset = CTLineGetOffsetForStringIndex(line, CTRunGetStringRange(run).location, NULL);
boundsRun.origin.x = point.x + xOffset;
boundsRun.origin.y = point.y - desecent;
//获取绘制路径
CGPathRef path = CTFrameGetPath(frame);
//获取剪裁区域边框
CGRect colRect = CGPathGetBoundingBox(path);
CGRect imageBounds = CGRectOffset(boundsRun, colRect.origin.x, colRect.origin.y);
return imageBounds;
}
}
return CGRectZero;
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint location = [self systemPointFromScreenPoint:[touch locationInView:self]];
//判断点击的是图片还是文字
if ([self checkIsClickOnImgWithPoint:location]) {
return;
}
[self clickOnStringWithPoint:location];
}
#pragma mark 转化成屏幕坐标
- (CGPoint)systemPointFromScreenPoint:(CGPoint)origin
{
return CGPointMake(origin.x, self.bounds.size.height - origin.y);
}
#pragma mark 点击图片
- (BOOL)checkIsClickOnImgWithPoint:(CGPoint)location
{
if ([self isFrame:_imgFrm containsPoint:location]) {
NSLog(@"你点击到了图片");
return YES;
}
return NO;
}
- (BOOL)isFrame:(CGRect)frame containsPoint:(CGPoint)point
{
return CGRectContainsPoint(frame, point);
}
/*
点击文字判断的大致步骤:
1.根据Frame拿到所有Line
2.计算每个Line中在全文的range
3.计算每个字对应line原点的X值
4.比对对应line的origin求得字对应起点坐标
5.求得下一个字的横坐标和上一行的origin,结合起点坐标得出字的坐标范围
6.屏幕坐标与drawRect坐标转换,判断是否在范围内
*/
#pragma mark 点击字符串 - (void)clickOnStringWithPoint:(CGPoint)point { //获取所有CTLine NSArray *lines = (NSArray *)CTFrameGetLines(self.ctFrm); //初始化范围数组 CFRange ranges[lines.count]; //初始化原点数组 CGPoint origins[lines.count]; //获取所有CTLine的原点 CTFrameGetLineOrigins(self.ctFrm, CFRangeMake(0, 0), origins); //获取每个CTLine中包含的富文本在整串富文本中的范围。将所有CTLine中字符串的范围保存下来放入数组中。 for (int i = 0; i < lines.count; i ++) { CTLineRef line = (__bridge CTLineRef)(lines[i]); CFRange range = CTLineGetStringRange(line); ranges[i] = range; } for (int i = 0;i < self.strLength; i ++) { long maxLoc; int lineNum; for (int j = 0; j < lines.count; j ++) { CFRange range = ranges[j]; maxLoc = range.location + range.length - 1; if (i <= maxLoc) { lineNum = j; break; } } CTLineRef line = (__bridge CTLineRef)(lines[lineNum]); CGPoint origin = origins[lineNum]; CGRect ctRunFrame = [self frameForCTRunWithIndex:i ctLine:line origin:origin]; if ([self isFrame:ctRunFrame containsPoint:point]) { NSLog(@"您点击到了第 %d 个字符,位于第 %d 行,然而他没有响应事件。",i,lineNum + 1); return; } } NSLog(@"您没有点击到文字"); } /** 字符Frame计算 @param index 索引 @param line 索引字符所在CTLine @param origin line的起点 @return 返回Frame */ - (CGRect)frameForCTRunWithIndex:(NSInteger)index ctLine:(CTLineRef)line origin:(CGPoint)origin { //获取字符起点相对于CTLine的原点的偏移量 CGFloat offsetX = CTLineGetOffsetForStringIndex(line, index, NULL); //获取下一个字符的偏移量,两者之间即为字符X范围 CGFloat offsetX2 = CTLineGetOffsetForStringIndex(line, index + 1, NULL); offsetX += origin.x; offsetX2 += origin.x; CGFloat offsetY = origin.y; CGFloat lineAscent,lineDescent; //获取当前点击的CTRun NSArray *runs = (NSArray *)CTLineGetGlyphRuns(line); CTRunRef runCurrent; for (int k = 0; k < runs.count; k ++) { CTRunRef run = (__bridge CTRunRef)(runs[k]); CFRange range = CTRunGetStringRange(run); NSRange rangeOC = NSMakeRange(range.location, range.length); if ([self isIndex:index inRange:rangeOC]) { runCurrent = run; break; } } //获得对应CTRun的尺寸信息 CTRunGetTypographicBounds(runCurrent, CFRangeMake(0, 0), &lineAscent, &lineDescent, NULL); //计算当前点击的CTRun高度 CGFloat height = lineAscent + lineDescent; return CGRectMake(offsetX, offsetY, offsetX2 - offsetX, height); } /** 范围检测 @param index 索引 @param range 范围 @return 范围内返回yes,否则返回no */ - (BOOL)isIndex:(NSInteger)index inRange:(NSRange)range { if ((index <= (range.location + range.length - 1)) && (index >= range.location)) { return YES; } return NO; }
- (void)dealloc
{
CFRelease(_ctFrm);
}
@end
相关文章推荐
- IOS CoreText系列四:图文混排之点击事件
- CoreText实现图文混排和点击事件
- iOS 疑难杂症(什么鬼系列)— — UITableView 添加 tableFooterView 旋转屏幕后收不到点击事件!!!
- Android自定义布局系列之一:流式布局(含TextView的点击事件)
- IOS CoreText系列一:理论
- iOS 仿斗鱼聊天:基于CoreText的面向对象图文排版工具AWRichText
- IOS CoreText系列一:理论
- Android TextView加载HTMl图文之添加点击事件和查看图片
- IOS CoreText系列二:简单使用
- IOS CoreText系列三:图文混排
- CoreText实现图文混排和点击事件
- iOS中textView和textField键盘的return键点击事件
- IOS CoreText --- 图文混排之代码封装
- IOS CoreText --- 支持图片及链接的点击
- IOS CoreText系列二:简单使用
- IOS CoreText --- 支持图片及链接的点击
- IOS CoreText系列三:图文混排
- 【iOS系列】-iOS查看沙盒文件图文教程(真机+模拟器)
- Android中为TextView中的部分文字设置颜色和点击事件
- ListView的Item里的TextView设置ClickableSpan和LinkMovementMethod导致ListView无法响应点击事件