美文网首页
DYFoldLabel -- 一行代码实现折叠label

DYFoldLabel -- 一行代码实现折叠label

作者: rookiesss | 来源:发表于2019-02-11 14:57 被阅读0次

源码在这里 ,使用Cocoapod:

pod 'DYFoldLabel'   

简介

设置一段显示不完整文字省略号后的折叠按钮。
先看效果:


效果图.gif

功能亮点:
1.用category实现,无需继承UILabel。
2.可以兼容自动布局。
3.解决大多数第三方文字点击在特殊情况有误问题。

方法调用

/**

 设置一段显示不完整文字省略号后的折叠按钮。

 @param foldText 折叠按钮文字
 @param font 折叠按钮文字字体
 @param color 折叠按钮文字颜色
 @param block 折叠按钮回调block,传入nil禁止点击
 */
- (void)setFoldText:(NSString *)foldText
           textFont:(UIFont *)font
          textColor:(UIColor *)color
         clickBlock:(DYFoldBtnClickBlock)block;

/**
 设置是否展开label。
 */
- (void)foldLabel:(BOOL)folded;

核心思路:

1.计算第几行结束。因为折叠文字的font可以和文本不一致,最后一行可能比其他行会占用更多行的空间。

- (NSInteger)lineReplaceWithLine:(CFIndex)lineCount lines:(CFArrayRef)lines fontDiff:(CGFloat)fontDiff {
    //计算单行高度
    CGFloat ascent = 0,descent = 0,leading = 0;
    CTLineRef line = CFArrayGetValueAtIndex(lines, 0);
    CTLineGetTypographicBounds(line, &ascent, &descent, &leading);
    CGFloat lineHeight = ascent + descent + leading;

    //计算最后一行的index。
    //eg:如果正常可以显示4行,折叠文字的font.poinSize = 20,正常文本的font.poinSize = 15,
    //那么最后一行会占用两行的高度,index就会比实际行数少1。
    NSInteger lineIndex = 0,index = 0;
    NSInteger number = self.numberOfLines - 1;
    CGFloat totalLineHeight = 0;
    while (totalLineHeight < (lineHeight + fontDiff) && lineCount >= index) {
        if (totalLineHeight < (lineHeight + leading)) {
            index++;
        }
        totalLineHeight = lineHeight * index;
    }
    lineIndex = lineCount - index;//这里需要+1,因为最后一行没计算在内,又因为数组最后一个元素索引=count-1,所以+1抵消
    lineIndex = (self.numberOfLines > 0) ? MIN(lineIndex, number) : lineCount;
    return lineIndex;
}

2.计算被替换文字的长度。要加省略号以及折叠文字,需要替换掉最后一行最后一部分文字。

- (NSInteger)subLenthWithString:(NSMutableAttributedString *)string lineRange:(NSRange)range text:(NSString *)text textFont:(UIFont *)font{
    //折叠文字宽度
    CGFloat foldWidth = [self dy_sizeForText:text Font:font size:CGSizeMake(self.bounds.size.width, CGFLOAT_MAX) mode:0].width;
    CGFloat spaceTextWidth = 0.0;
    NSInteger index = 0;
    while (spaceTextWidth < foldWidth) {
        NSString *spaceText = [string attributedSubstringFromRange:NSMakeRange(range.location + range.length - index, index)].string;
        spaceTextWidth = [self dy_sizeForText:spaceText Font:self.font size:CGSizeMake(self.bounds.size.width, CGFLOAT_MAX) mode:0].width;
        index++;
    }
    return index;
}

3.点击事件。市面上大多数第三方ULabel部分文字的点击事件在自动布局情况下会出错,因为在label大小(下图绿色部分)大于文字所能占的大小时,文字会居中,coreText获取的位置是文字在label的top位置的坐标,在判断点击位置时不准确。
如图:


label大于文字范围.png

实现如下:

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
    DYFoldBtnClickBlock clickBlock = objc_getAssociatedObject(self, "clickBlock");
    if (!clickBlock || self.isFolded) return;
    UITouch *touch = [touches anyObject];
    CGPoint location = [touch locationInView:self];
    CGPoint clickPoint = CGPointMake(location.x, self.bounds.size.height - location.y);
    CTFrameRef _frame = (__bridge CTFrameRef)(objc_getAssociatedObject(self, "frameRef"));
    
    //获取最后行
    CFIndex endLineIndex = [objc_getAssociatedObject(self, "endLineIndex") integerValue];
    CFArrayRef lines = CTFrameGetLines(_frame);
    
    //获取行上行、下行、间距
    CGFloat ascent = 0;
    CGFloat descent = 0;
    CGFloat leading = 0;
    CTLineRef endLine = CFArrayGetValueAtIndex(lines, endLineIndex);
    CTLineGetTypographicBounds(endLine, &ascent, &descent, &leading);
    
    UIFont *font = objc_getAssociatedObject(self, "foldFont");
    CGFloat endLineHeight = leading + MAX(font.pointSize, self.font.pointSize);
    //计算点击位置是否在折叠行内
    CGPoint origins[CFArrayGetCount(lines)];
    CTFrameGetLineOrigins(_frame, CFRangeMake(0, 0), origins);
    CGPoint endLineOrigin = origins[endLineIndex];
    //显示的文本高度
    CGFloat textHeight = self.bounds.size.height - endLineOrigin.y + endLineHeight;
    //最后一行的范围判断。
    if (clickPoint.y <= (self.bounds.size.height * 0.5 - textHeight * 0.5 + endLineHeight) && clickPoint.y >= (self.bounds.size.height * 0.5 - textHeight * 0.5 )) {
        CFIndex index = CTLineGetStringIndexForPosition(endLine, clickPoint);
        NSString *foldText = objc_getAssociatedObject(self, "foldText");
        NSRange range = NSMakeRange(self.text.length - foldText.length, foldText.length);
        //判断点击的字符是否在需要处理点击事件的字符串范围内
        if (range.location <= index) {
            if (clickBlock) {
                clickBlock();
            }
        }
    }
}

如果对你有帮助的话,能给 DYFoldLabel 打赏一个小星星吗💕

相关文章

网友评论

      本文标题:DYFoldLabel -- 一行代码实现折叠label

      本文链接:https://www.haomeiwen.com/subject/peqqeqtx.html