轻量级计算点击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就是我们需要的文字索引

 

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM