今天教大家怎么利用CoreText(有关CoreText框架的知识有兴趣的同学学学习)计算点击label,算出你点击的文字在哪个位置,废话不多说,直接开始
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就是我们需要的文字索引