轻量级计算点击UILabel中的文字位置
2016-04-08 11:32
323 查看
今天教大家怎么利用CoreText(有关CoreText框架的知识有兴趣的同学学学习)计算点击label,算出你点击的文字在哪个位置,废话不多说,直接开始
1,首先我们要拿到你所点击的点point,我们在touch事件里面取
这个point就是我们要取的点
2,我们根据这个点来计算当前点击的文字的index
3,然后我们要重写
- (CGRect)textRectForBounds:(CGRect)bounds
limitedToNumberOfLines:(NSInteger)numberOfLines这个方法
4,我们要考虑文字对齐方式的影响
5,我们来调用
CFIndex idx=[self characterIndexAtPoint:point];拿到的idx就是我们需要的文字索引
1,首先我们要拿到你所点击的点point,我们在touch事件里面取
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{ UITouch *touch=[touches anyObject]; CGPoint point=[touch locationInView:self]; }
这个point就是我们要取的点
2,我们根据这个点来计算当前点击的文字的index
- (CFIndex)characterIndexAtPoint:(CGPoint)p { if (!CGRectContainsPoint(self.bounds, p)) { return NSNotFound; } CGRect textRect = [self textRectForBounds:self.bounds limitedToNumberOfLines:self.numberOfLines]; if (!CGRectContainsPoint(textRect, p)) { return NSNotFound; } // Offset tap coordinates by textRect origin to make them relative to the origin of frame p = CGPointMake(p.x - textRect.origin.x, p.y - textRect.origin.y); // Convert tap coordinates (start at top left) to CT coordinates (start at bottom left) p = CGPointMake(p.x, textRect.size.height - p.y); CGMutablePathRef path = CGPathCreateMutable(); CGPathAddRect(path, NULL, textRect); CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef)self.attributedText); CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, (CFIndex)[self.attributedText length]), path, NULL); if (frame == NULL) { CGPathRelease(path); return NSNotFound; } CFArrayRef lines = CTFrameGetLines(frame); NSInteger numberOfLines = (self.numberOfLines > 0 ? MIN(self.numberOfLines, CFArrayGetCount(lines)) : CFArrayGetCount(lines)); if (numberOfLines == 0) { CFRelease(frame); CGPathRelease(path); return NSNotFound; } CFIndex idx = NSNotFound; CGPoint lineOrigins[numberOfLines]; CTFrameGetLineOrigins(frame, CFRangeMake(0, numberOfLines), lineOrigins); for (CFIndex lineIndex = 0; lineIndex < numberOfLines; lineIndex++) { CGPoint lineOrigin = lineOrigins[lineIndex]; //lineOrigin.y-=(numberOfLines-1)*[self lineSp]; CTLineRef line = CFArrayGetValueAtIndex(lines, lineIndex); // Get bounding information of line CGFloat ascent = 0.0f, descent = 0.0f, leading = 0.0f; CGFloat width = (CGFloat)CTLineGetTypographicBounds(line, &ascent, &descent, &leading); CGFloat yMin = (CGFloat)floor(lineOrigin.y - descent); CGFloat yMax = (CGFloat)ceil(lineOrigin.y + ascent); // Apply penOffset using flushFactor for horizontal alignment to set lineOrigin since this is the horizontal offset from drawFramesetter CGFloat flushFactor = TTTFlushFactorForTextAlignment(self.textAlignment); CGFloat penOffset = (CGFloat)CTLineGetPenOffsetForFlush(line, flushFactor, textRect.size.width); lineOrigin.x = penOffset; // Check if we've already passed the line if (p.y > yMax) { break; } // Check if the point is within this line vertically if (p.y >= yMin) { // Check if the point is within this line horizontally if (p.x >= lineOrigin.x && p.x <= lineOrigin.x + width) { // Convert CT coordinates to line-relative coordinates CGPoint relativePoint = CGPointMake(p.x - lineOrigin.x, p.y - lineOrigin.y); idx = CTLineGetStringIndexForPosition(line, relativePoint); break; } } } CFRelease(framesetter); CFRelease(frame); CGPathRelease(path); DLog(@"点击index:%ld",idx); return idx; }
3,然后我们要重写
- (CGRect)textRectForBounds:(CGRect)bounds
limitedToNumberOfLines:(NSInteger)numberOfLines这个方法
- (CGRect)textRectForBounds:(CGRect)bounds limitedToNumberOfLines:(NSInteger)numberOfLines { bounds = UIEdgeInsetsInsetRect(bounds, UIEdgeInsetsZero); if (!self.attributedText) { return [super textRectForBounds:bounds limitedToNumberOfLines:numberOfLines]; } CGRect textRect = bounds; // Calculate height with a minimum of double the font pointSize, to ensure that CTFramesetterSuggestFrameSizeWithConstraints doesn't return CGSizeZero, as it would if textRect height is insufficient. textRect.size.height = MAX(self.font.lineHeight * MAX(2, numberOfLines), bounds.size.height); // Adjust the text to be in the center vertically, if the text size is smaller than bounds CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef)self.attributedText); CGSize textSize = CTFramesetterSuggestFrameSizeWithConstraints(framesetter, CFRangeMake(0, (CFIndex)[self.attributedText length]), NULL, textRect.size, NULL); textSize = CGSizeMake(ceil(textSize.width), ceil(textSize.height)); // Fix for iOS 4, CTFramesetterSuggestFrameSizeWithConstraints sometimes returns fractional sizes if (textSize.height < bounds.size.height) { CGFloat yOffset = 0.0f; switch (0) { case 0: yOffset = floor((bounds.size.height - textSize.height) / 2.0f); break; case 1: yOffset = bounds.size.height - textSize.height; break; case 2: default: break; } textRect.origin.y += yOffset; } CFRelease(framesetter); return textRect; }
4,我们要考虑文字对齐方式的影响
static inline CGFloat TTTFlushFactorForTextAlignment(NSTextAlignment textAlignment) { switch (textAlignment) { case NSTextAlignmentCenter: return 0.5f; case NSTextAlignmentRight: return 1.0f; case NSTextAlignmentLeft: default: return 0.0f; } }
5,我们来调用
CFIndex idx=[self characterIndexAtPoint:point];拿到的idx就是我们需要的文字索引
相关文章推荐
- query操作的加锁过程和时间统计
- mui禁止横屏显示,仅支持竖屏显示
- 使用webpack,vue文件导入样式文件报错
- UILabel
- UITableView简单介绍
- iOS设置UILabel的行间距
- LeetCode(36)- Implement Stack using Queues
- LeetCode(36)- Implement Stack using Queues
- LeetCode(36)- Implement Stack using Queues
- xcodebuild error: unable to rename temporary
- java之Continue解析
- dequeueReusableCellWithIdentifier和dequeueReusableCellWithIdentifier:forIndexPath的区别
- 使用Autolayout实现UITableView的Cell动态布局和高度动态改变
- iPhone/iPad/Android UI尺寸规范
- require和include的区别
- 2016.04.07,英语,《Vocabulary Builder》Unit 11
- iOS开发技巧(系列十二:UUID和UDID的区别)
- 标准模板库(STL)之 priority_queue 列传
- 黑暗料理一之修改UITableViewCell左滑删除按钮的样式和自定义
- LeetCode 128. Longest Consecutive Sequence