zoukankan      html  css  js  c++  java
  • iOS 图文混排

    使用系统自带的NSAttributedString来处理,对于一般的图文混排已经足够了,但是,有一个缺点就是NSAttributedString并不支持gif动画。实际上,使用gif动画还是挺卡的。

    思路:

    1.通过RegexKitLite 正则,匹配出所有需要特殊处理的字符

    2.由于表情图片占用一个字符,使用直接替换范围的方式,会导致后面的表情范围不对。有两种处理方案

    方案一: 

    • 使用两个数组,分别装特殊字符(文字内容,文字范围,是否为特殊字符,是否为表情)和非特殊字符,按范围排序成一个新数组
    • 循环新数组List,通过判断是否为表情和是否为特殊字符来添加appendAttributedString属性。

    方案二:

    通过递归的方式,每找到一个表情,先替换掉,再递归找下一个

    当前,使用的是方案一,核心代码:

    /**
     *  普通文字 --> 属性文字
     *
     *  @param text 普通文字
     *
     *  @return 属性文字
     */
    - (NSAttributedString *)attributedTextWithText:(NSString *)text
    {
        NSMutableAttributedString *attributedText = [[NSMutableAttributedString alloc] init];
        
        // 表情的规则
        NSString *emotionPattern = @"\[[0-9a-zA-Z\u4e00-\u9fa5]+\]";
        // @的规则
        NSString *atPattern = @"@[0-9a-zA-Z\u4e00-\u9fa5-_]+";
        // #话题#的规则
        NSString *topicPattern = @"#[0-9a-zA-Z\u4e00-\u9fa5]+#";
        // url链接的规则
        NSString *urlPattern = @"\b(([\w-]+://?|www[.])[^\s()<>]+(?:\([\w\d]+\)|([^[:punct:]\s]|/)))";
        NSString *pattern = [NSString stringWithFormat:@"%@|%@|%@|%@", emotionPattern, atPattern, topicPattern, urlPattern];
        
        // 遍历所有的特殊字符串
        NSMutableArray *parts = [NSMutableArray array];
        [text enumerateStringsMatchedByRegex:pattern usingBlock:^(NSInteger captureCount, NSString *const __unsafe_unretained *capturedStrings, const NSRange *capturedRanges, volatile BOOL *const stop) {
            if ((*capturedRanges).length == 0) return;
            
            TextPartModel *part = [[TextPartModel alloc] init];
            part.special = YES;
            part.text = *capturedStrings;
            part.emotion = [part.text hasPrefix:@"["] && [part.text hasSuffix:@"]"];
            part.range = *capturedRanges;
            [parts addObject:part];
        }];
        
        // 遍历所有的非特殊字符
        [text enumerateStringsSeparatedByRegex:pattern usingBlock:^(NSInteger captureCount, NSString *const __unsafe_unretained *capturedStrings, const NSRange *capturedRanges, volatile BOOL *const stop) {
            if ((*capturedRanges).length == 0) return;
            
            TextPartModel *part = [[TextPartModel alloc] init];
            part.text = *capturedStrings;
            part.range = *capturedRanges;
            [parts addObject:part];
        }];
        
        // 排序
        // 系统是按照从小 -> 大的顺序排列对象
        [parts sortUsingComparator:^NSComparisonResult(TextPartModel *part1, TextPartModel *part2) {
            // NSOrderedAscending = -1L, NSOrderedSame, NSOrderedDescending
            // 返回NSOrderedSame:两个一样大
            // NSOrderedAscending(升序):part2>part1
            // NSOrderedDescending(降序):part1>part2
            if (part1.range.location > part2.range.location) {
                // part1>part2
                // part1放后面, part2放前面
                return NSOrderedDescending;
            }
            // part1<part2
            // part1放前面, part2放后面
            return NSOrderedAscending;
        }];
        
        UIFont *font = [UIFont systemFontOfSize:15];
        NSMutableArray *specials = [NSMutableArray array];
        // 按顺序拼接每一段文字
        for (TextPartModel *part in parts) {
            // 等会需要拼接的子串
            NSAttributedString *substr = nil;
            if (part.isEmotion) { // 表情
                NSTextAttachment *attch = [[NSTextAttachment alloc] init];
                NSString *name = [EmoticonTool emoticonWithChs:part.text].png;
                if (name) { // 能找到对应的图片
                    attch.bounds = CGRectMake(0, -3, font.lineHeight, font.lineHeight);
                    attch.image = [UIImage imageNamed:name];
                    substr = [NSAttributedString attributedStringWithAttachment:attch];
                } else { // 表情图片不存在
                    substr = [[NSAttributedString alloc] initWithString:part.text];
                }
            } else if (part.special) { // 非表情的特殊文字
                substr =[[NSAttributedString alloc] initWithString:part.text attributes:@{NSForegroundColorAttributeName : [UIColor redColor]
                                                                                          }];
                SpecialModel *s = [[SpecialModel alloc] init];
                s.text = part.text;
                NSUInteger loc = attributedText.length;
                NSUInteger len = part.text.length;
                s.range = NSMakeRange(loc, len);
                [specials addObject:s];
            } else { // 非特殊文字
                substr = [[NSAttributedString alloc] initWithString:part.text];
            }
            [attributedText appendAttributedString:substr];
        }
        
        // 一定要设置字体,保证计算出来的尺寸是正确的
        [attributedText addAttribute:NSFontAttributeName value:font range:NSMakeRange(0, attributedText.length)];
        [attributedText addAttribute:@"specials" value:specials range:NSMakeRange(0, 1)];
        
        return attributedText;
    }
    View Code

     在使用过程中,我们需要注意一点,计算文本的宽高,通过下面来计算

     [status.attributedText boundingRectWithSize:CGSizeMake(maxW, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin context:nil].size;

    在计算过程中,我们并没有传字体大小,因而,我们需要给attributedText一开始设定字体大小:

    // 一定要设置字体,保证计算出来的尺寸是正确的
        [attributedText addAttribute:NSFontAttributeName value:font range:NSMakeRange(0, attributedText.length)];

    设置行高

        // 定义行高
        NSMutableParagraphStyle * paragraphStyle = [[NSMutableParagraphStyle alloc] init];
        [paragraphStyle setLineSpacing:5];
        [attributedText addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:NSMakeRange(0, attributedText.length)];

    附源代码:http://pan.baidu.com/s/1o8Su8H0

    目录说明:

    HomeViewController  ---列表

    StatusCell       ---列表TableCell

    StatusFrame       ---列表TableCell高度。由于cell的高度是不固定的,因此我们定义StatusFrame来管理所有的控件尺寸,最后返回总高度

    StatusModel      ---微博模型,图文混排处理,在这里做核心混排处理,通过添加attributedText属性处理。

      --TextPartModel  --StatusModel嵌套属性,用于记录RegexKitLite 正则匹配出的字符

      --SpecialModel   --StatusModel嵌套属性,用于特殊实符点击变色的范围比比较

      --UserModel     --StatusModel嵌套属性,用户模型

  • 相关阅读:
    React native 之 图标库ECharts的使用
    使用jquery给html标签加点击事件
    React native 之 async/await
    CSS布局之flexbox
    Swiper 的引入
    给浏览器设置一张背景图,并且拉动浏览器大小时图片不要被压缩变形
    境界的彼方_lduoj_bfs宽搜
    2021美国大学生数学建模大赛==ABCDEF+思路解析==
    3045 Lcm与Gcd构造
    对拍程序
  • 原文地址:https://www.cnblogs.com/jys509/p/5539217.html
Copyright © 2011-2022 走看看