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

     

     

  • 相关阅读:
    A*算法在栅格地图上的路径搜索(python实现)_1.1
    python基础
    Celery ---异步任务,定时任务,周期任务
    Flask-Script
    Flask-SQLAlchemy
    SQLAlchemy的增删改查 一对多 多对多
    Django Rest framework
    django之forms组件
    缓存, 队列(Redis,RabbitMQ)
    django框架(2)
  • 原文地址:https://www.cnblogs.com/wjblogs/p/5367329.html
Copyright © 2020-2023  润新知