使用YYLabel来实现收起和展开,嵌入#话题并允许点击

声明:作者声明此文章为原创,未经作者同意,请勿转载,若转载,务必注明本站出处,本平台保留追究侵权法律责任的权利。
全栈老韩
全栈工程师,擅长iOS App开发、前端(vue、react、nuxt、小程序&Taro)开发、Flutter、React Native、后端(midwayjs、golang、express、koa)开发、docker容器、seo优化等。

总结:这里实现的方式使用了numberOfLines属性,配合YYText的富文本增加点击事件,以及Masory布局来实现展开和收起,以及点击话题。
如果需要控制折叠状态下的文本字数,可以直接截取富文本的某一段就可以了,这里不赘述。

一、依赖 YYText

pod 'YYText'

二、实例化YYLabel作为文本组件

注意:要设置一下preferredMaxLayoutWidth,控制文本的宽度
(以下代码中的颜色和字体可根据自己需要自行设置,这里是使用我们自己封装的字体和颜色)

- (YYLabel *)descLabel {
    if (!_descLabel) {
        _descLabel = [[YYLabel alloc] init];
        _descLabel.font = [DZFontStyle pingFangFontOfSize:14];
        _descLabel.textColor = UIColorWithHex(@"#E0E1E6");
        _descLabel.textAlignment = NSTextAlignmentLeft;
        _descLabel.numberOfLines = 2;
        _descLabel.lineBreakMode = NSLineBreakByTruncatingTail;
        _descLabel.preferredMaxLayoutWidth = CONTAINER_WIDTH;
    }
    return _descLabel;
}

三、获取富文本

1.第一步就是将整个的普通字符串,添加字体、颜色属性成富文本,再链接append上话题的富文本,形成了一个完整的富文本;
2.这一步需要设置展开状态下的富文本:
普通字符串富文本 + #话题#富文本及点击事件 + 省略号...(可选,这里我使用了空格) + “收起”image及点击事件
3.折叠状态下的富文本,就是完整的富文本;(折叠状态下显示“展开”的操作在下一节)
4.避免重复计算展开和折叠状态下的富文本,所以使用2个变量接收第一次算出来的富文本
5.在label所在的代码作用域,实现“收起”的点击事件(下面代码是用block交给外层)

/// 处理图片描述富文本
- (NSAttributedString *)videoLayerDescAttStrWithDescWidth:(CGFloat)descWidth {
    // 普通字符串
    NSString *allStr = DZRealString(self.videoInfoVO.des);
    // 富文本
    NSMutableAttributedString *attStr = [[NSMutableAttributedString alloc] initWithString:allStr];
    NSRange range = NSMakeRange(0, attStr.length);
    if (range.location != NSNotFound) {
        [attStr addAttribute:NSFontAttributeName value:Font(14) range:range];
        [attStr addAttribute:NSForegroundColorAttributeName value:UIColorFromRGB(0xE0E1E6) range:range];
    }
    //话题,如果存在,添加点击方法(这里使用block交给外部实现),就append起来
    NSString *topicStr = nil;
    NSMutableAttributedString *topicAttStr = nil;
    if (!IsEmpty(self.topicInfoVO)) {
        topicStr = [NSString stringWithFormat:@"#%@#", DZRealString(self.topicInfoVO.title)];
        topicAttStr = [[NSMutableAttributedString alloc] initWithString:topicStr attributes:@{NSFontAttributeName:[DZFontStyle pingFangMediumFontOfSize:14], NSForegroundColorAttributeName:UIColorFromRGB(0xFFFFFF)}];
        @weakify_dzx(self);
        [topicAttStr yy_setTextHighlightRange:NSMakeRange(0, topicAttStr.length) color:UIColorFromRGB(0xFFFFFF) backgroundColor:[UIColor clearColor] tapAction:^(UIView * _Nonnull containerView, NSAttributedString * _Nonnull text, NSRange range, CGRect rect) {
            @strongify_dzx(self);
            if (self.topicClick) {
                self.topicClick();
            }
        }];
        [attStr appendAttributedString:topicAttStr];
    }
    // 这里是2行的宽度,减去"收起"图片的宽度,就是展示整个富文本的总宽度
    CGFloat descTwoLineMaxWidth = descWidth * 2.0 - 35;
    // 富文本需要展示完全的宽度
    CGRect fullStrRectInOneLine = [attStr boundingRectWithSize:CGSizeMake(MAXFLOAT, MAXFLOAT) options:(NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading) context:nil];
    
    if (fullStrRectInOneLine.size.width > descTwoLineMaxWidth) { // 超出2行 折叠起来了
        if (self.isExpand) {
            if (self.expandDescAttri) {
                return self.expandDescAttri;
            } else {
                // 这里控制的是,让"展开"状态下的富文本最多字数为100,
                if (attStr.length > 100) {
                    attStr = [[NSMutableAttributedString alloc] initWithAttributedString:[attStr attributedSubstringFromRange:NSMakeRange(0, 100)]];
                }
                // append 空格
                [attStr appendAttributedString:[[NSAttributedString alloc] initWithString:@" " attributes:@{NSForegroundColorAttributeName : [UIColor whiteColor], NSFontAttributeName : Font(14)}]];
                // append "收起"图片的attachment
                NSAttributedString *imgAttri = [NSAttributedString yy_attachmentStringWithContent:[UIImage imageNamed:@"immersion_video_desc_collapse"] contentMode:UIViewContentModeCenter attachmentSize:CGSizeMake(35, 16) alignToFont:[DZFontStyle pingFangFontOfSize:16] alignment:(YYTextVerticalAlignmentCenter)];
                [attStr appendAttributedString:imgAttri];
                // 点击"收起"的事件
                @weakify_dzx(self);
                NSRange attachmentRange = NSMakeRange(attStr.length - 1, 1);
                [attStr yy_setTextHighlightRange:attachmentRange color:nil backgroundColor:nil tapAction:^(UIView * _Nonnull containerView, NSAttributedString * _Nonnull text, NSRange range, CGRect rect) {
                    @strongify_dzx(self);
                    if (self.videoCollapseClick) {
                        self.videoCollapseClick();
                    }
                }];
                // 拼接成最终的富文本
                attStr = [[NSMutableAttributedString alloc] initWithAttributedString:[[self class] addParagraphForAttri:attStr]];
                self.expandDescAttri = attStr;
                return attStr;
            }
        } else {
            if (self.foldDescAttri) {
                return self.foldDescAttri;
            } else {
                // 完整的富文本
                attStr = [[NSMutableAttributedString alloc] initWithAttributedString:[[self class] addParagraphForAttri:attStr]];
                self.foldDescAttri = attStr;
                return attStr;
            }
        }
    } else { // 未超出2行
        return attStr;
    }
    return attStr;
}
// 这里加了富文本的行间距属性
+ (NSAttributedString *)addParagraphForAttri:(NSAttributedString *)attStr {
    NSRange attRange = NSMakeRange(0, attStr.length);
    if (attRange.location != NSNotFound) {
        NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
        paragraphStyle.lineSpacing = 5;
        paragraphStyle.paragraphSpacing = 0;
        paragraphStyle.lineBreakMode = NSLineBreakByTruncatingTail;
        NSMutableAttributedString *attributedStr = [[NSMutableAttributedString alloc] initWithAttributedString:attStr];
        [attributedStr addAttributes:@{ NSParagraphStyleAttributeName : paragraphStyle} range:attRange];
        return attributedStr;
    }
    return attStr;
}
  1. 收起事件的实现
// "收起"
    infoVo.videoCollapseClick = ^{
        weakSelf.infoVo.isExpand = NO;
        weakSelf.descLabel.numberOfLines = 2;
        weakSelf.descLabel.attributedText = [weakSelf.infoVo videoLayerDescAttStrWithDescWidth:CONTAINER_WIDTH];
    };

四、折叠状态下的“展开”+ 点击展开事件

  1. 使用YYLabel的truncationToken,来显示“展开”。
// 这里使用变量属性truncationToken,后面会赋值给YYLabel
- (NSAttributedString *)truncationToken{
    if (!_truncationToken) {
        // 展开图片的size
        CGSize imageSize = CGSizeMake(57, 27);
        // 容器view,子视图是省略号+展开image
        UIView *trailingView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, imageSize.width, imageSize.height)];
        // 给容器视图加点击事件,点击后就显示展开状态下的富文本
        @weakify_dzx(self);
        [trailingView addTapGestureActionWithBlock:^(UITapGestureRecognizer * _Nonnull tapAction) {
            @strongify_dzx(self);
            [self tapExpandAction];
        }];
        // 省略号
        UILabel *dotLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 6, 22, 17)];
        dotLabel.font = [DZFontStyle pingFangFontOfSize:14];
        dotLabel.textColor = [UIColor whiteColor];
        dotLabel.text = @"...";
        // 展开image
        UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"immersion_video_desc_expand"]];
        imageView.frame = CGRectMake(22, 6, 35, 16);
        // 添加进容器
        [trailingView addSubview:dotLabel];
        [trailingView addSubview:imageView];
        
        _truncationToken = [NSAttributedString yy_attachmentStringWithContent:trailingView contentMode:UIViewContentModeCenter attachmentSize:imageSize alignToFont:[DZFontStyle pingFangFontOfSize:16] alignment:(YYTextVerticalAlignmentCenter)];
    }
    return _truncationToken;
}
  1. 赋值truncationToken
self.descLabel.truncationToken = self.truncationToken;
  1. 展开的实现
/// 点击展开
- (void)tapExpandAction {
    self.infoVo.isExpand = YES;
    self.descLabel.numberOfLines = 0; // 不限行
    self.descLabel.attributedText = [self.infoVo videoLayerDescAttStrWithDescWidth:CONTAINER_WIDTH];
}

五、给YYLabel赋值

// 描述
    self.descLabel.attributedText = [infoVo videoLayerDescAttStrWithDescWidth:CONTAINER_WIDTH];

暂无评论,快来发表第一条评论吧