轻量级计算点击UILabel中的文字位置
今天教大家怎么利用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就是我们需要的文字索引