iOS NSString和NSAttributedString相互转换(更新版)

我更新了啥?

我只是最近学会了的排版,于是选了我比较受关注的一篇重新排了一下版(确实美观了很多),顺便再记录了一下我处理云旺表情时遇到无法正则时的处理办法,仅此而已

NSString转NSAttributedString

最近研究了含表情的字符串,我们实现像的其实不难,输入法都支持,但是如果想换成自己需要的图片表情,甚至GIF动画表情,就要花一番功夫了。除了利用webview写HTML的方法外,有一种更加清明的方法,那就是属性字符串,即NSAttributedString、NSMutableAttributedString,顾名思义后者就是可变的而已。

首先,先实现普通字符串转属性字符串吧(copy的):

+ (NSMutableAttributedString *)stringToAttributeString:(NSString *)text{
    //先把普通的字符串text转化生成Attributed类型的字符串
    NSMutableAttributedString * attStr = [[NSMutableAttributedString alloc]initWithString:text];
    //正则表达式 ,例如  [(呵呵)] = 
    NSString * zhengze = @"\\[\\([a-zA-Z0-9\u4e00-\u9fa5]+\\)\\]";
    NSError * error;
    NSRegularExpression * re = [NSRegularExpression regularExpressionWithPattern:zhengze options:NSRegularExpressionCaseInsensitive error:&error];
    if (!re)
    {
        //打印错误
        NSLog(@"error=%@",[error localizedDescription]);
    }
    NSArray * arr = [re matchesInString:text options:0 range:NSMakeRange(0, text.length)];//遍历字符串,获得所有的匹配字符串
    NSBundle *bundle = [NSBundle mainBundle];
    NSString * path = [bundle pathForResource:@"emj" ofType:@"plist"];  //plist文件,制作一个 数组,包含文字,表情图片名称
    NSArray * face = [[NSArray alloc]initWithContentsOfFile:path];//获取 所有的数组
    //如果有多个表情图,必须从后往前替换,因为替换后Range就不准确了
    for (int j =(int) arr.count - 1; j >= 0; j--) {
        //NSTextCheckingResult里面包含range
        NSTextCheckingResult * result = arr[j];
        for (int i = 0; i < face.count; i++) {
            if ([[text substringWithRange:result.range] isEqualToString:face[i][@"key"]])//从数组中的字典中取元素
            {
                NSString * imageName = [NSString stringWithString:face[i][@"picture"]];
                //添加附件,图片
                NSTextAttachment * textAttachment = [[NSTextAttachment alloc]init];
                //调节表情大小
                textAttachment.bounds=CGRectMake(0, 0, 20, 20);
                textAttachment.image = [UIImage imageNamed:imageName];
                NSAttributedString * imageStr = [NSAttributedString attributedStringWithAttachment:textAttachment];
                //替换未图片附件
                [attStr replaceCharactersInRange:result.range withAttributedString:imageStr];
                break;
            }
        }
    }
    return attStr;
}

NSAttributedString转NSString

=====================================
当然,你肯定会需要转回来:

//把带有图片的属性字符串转成普通的字符串
+ (NSString *)textString:(NSAttributedString *)attributedText
{
    NSMutableAttributedString * resutlAtt = [[NSMutableAttributedString alloc]initWithAttributedString:attributedText];
    EmoticonsHelper * helper = [EmoticonsHelper new];
    //枚举出所有的附件字符串
    [attributedText enumerateAttributesInRange:NSMakeRange(0, attributedText.length) options:NSAttributedStringEnumerationReverse usingBlock:^(NSDictionary *attrs, NSRange range, BOOL *stop) {
        //从字典中取得那一个图片
        NSTextAttachment * textAtt = attrs[@"NSAttachment"];
        if (textAtt)
        {
            UIImage * image = textAtt.image;
            NSString * text = [helper stringFromImage:image];
            [resutlAtt replaceCharactersInRange:range withString:text];
        }
    }];
    return resutlAtt.string;
}

其中EmoticonsHelper实现了图片和字符串的一一对应,和需求相关就不贴代码了.

含表情字符串的尺寸计算

会不会需要属性字符串的尺寸大小呢?不用着急,直接有代码:

+(CGSize)getAttributedTextSize:(NSString *)text
{
    //先把普通的字符串text转化生成Attributed类型的字符串
    NSMutableAttributedString * attStr = [[NSMutableAttributedString alloc]initWithString:text];
    NSString * zhengze = @"\\[\\([a-zA-Z0-9\u4e00-\u9fa5]+\\)\\]";
    NSError * error;
    NSRegularExpression * re = [NSRegularExpression regularExpressionWithPattern:zhengze options:NSRegularExpressionCaseInsensitive error:&error];
    if (!re)
    {
        NSLog(@"正则表达式匹配错误%@" ,[error localizedDescription]);
    }
    NSArray * arr = [re matchesInString:text options:0 range:NSMakeRange(0, text.length)];
    if (!arr.count)//说明字符串中没有表情通配符,是普通的文本,则计算文本size
    {
        NSDictionary *dic=@{NSFontAttributeName: [UIFont systemFontOfSize:14]};
        CGSize size1=[text boundingRectWithSize:CGSizeMake(160, 1000) options:NSStringDrawingUsesLineFragmentOrigin attributes:dic context:nil].size;
        if (size1.height<=60)
        {
            size1.height=60;
        }else
        {
            size1.height+=15;
        }
        return size1;
    }
    NSBundle *bundle = [NSBundle mainBundle];
    NSString * path = [bundle pathForResource:@"emj" ofType:@"plist"];
    NSArray * face = [[NSArray alloc]initWithContentsOfFile:path];
    //如果有多个表情图,必须从后往前替换,因为替换后Range就不准确了
    for (int j =(int) arr.count - 1; j >= 0; j--) {
        //NSTextCheckingResult里面包含range
        NSTextCheckingResult * result = arr[j];
        for (int i = 0; i < face.count; i++) {
            if ([[text substringWithRange:result.range] isEqualToString:face[i][@"key"]])
            {
                NSString * imageName = [NSString stringWithString:face[i][@"picture"]];
                NSTextAttachment * textAttachment = [[NSTextAttachment alloc]init];
                textAttachment.image = [UIImage imageNamed:imageName];
                NSAttributedString * imageStr = [NSAttributedString attributedStringWithAttachment:textAttachment];
                [attStr replaceCharactersInRange:result.range withAttributedString:imageStr];
                break;
            }
        }
    }
    CGSize size2 = [attStr boundingRectWithSize:CGSizeMake(180, 1000) options:NSStringDrawingUsesLineFragmentOrigin context:nil].size;
    size2.height+=40;  //表情文字增加高度
    return size2;//返回属性字符串的尺寸
}

输入时显示表情

当然,以上这些都是网上随便可以找的到的,下面要写一点不一样的东西了
首先我们知道和字符串相关的UI控件呢不外乎那么几种,怎么样利用上属性字符串呢?

UITextView、UITextField

以UITextView为例,要表情图片神奇的显示在输入中,你需要如此做:

- (void)emoticonAttributedStringTextView:(UITextView *)textView andAttributedString:(NSAttributedString *)attributedString{
    [textView.textStorage insertAttributedString:attributedString atIndex:textView.selectedRange.location];
    textView.selectedRange = NSMakeRange(textView.selectedRange.location+1, 0);
    //重置格式
    NSRange wholeRange = NSMakeRange(0,textView.textStorage.length);
    [textView.textStorage removeAttribute:NSFontAttributeName range:wholeRange];
    [textView.textStorage addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:20.0f] range:wholeRange];
}

这样不但能显示表情图片到你的TextView里,还能通过UIFont
设置显示的大小。

UILabel

这个控件就不用多说了,直接有个属性名字叫attributedText,直接赋值就能显示了。

附上 demo

另外,附上做云旺表情支持的时候的代码,仅供参考

- (NSMutableAttributedString *)stringToAttributeString:(NSString *)text{
    //先把普通的字符串text转化生成Attributed类型的字符串
    NSDictionary *attribs = @{NSFontAttributeName: messageFont};
    if(![XFTools textNotEmpty:text]){
        text = @"";
    }
    NSMutableAttributedString * attStr = [[NSMutableAttributedString alloc]initWithString:text attributes:attribs];
    NSArray *arrEmo =[[YWEmoticonFactory sharedInstance]shareEmoticonsInfoWithType:YWEmoticonTypeStaticImage];
    NSArray *arrEmo2 =[[YWEmoticonFactory sharedInstance]shareEmoticonsSymbolsWithType:YWEmoticonTypeStaticImage];
    NSMutableDictionary *dic =[[YWEmoticonFactory sharedInstance]shareEmoticonImagesWithType:YWEmoticonTypeStaticImage];
    for (int j = 0; j < arrEmo2.count; ) {
        NSString *subStr = arrEmo2[j];
        NSRange subRange = [attStr.string rangeOfString:subStr];
        if (subRange.location!=NSNotFound) {
            for (int i = 0; i < arrEmo.count; i++) {
                YWEmoticon *emo = arrEmo[i];
                if ([subStr isEqualToString:emo.emoticon])
                {
                    NSString * imageName = emo.fileName;
                    NSTextAttachment * textAttachment = [[NSTextAttachment alloc]init];
                    textAttachment.bounds=CGRectMake(0, 2, 17, 17);
                    textAttachment.image =[dic objectForKey:imageName];
                    NSAttributedString * imageStr = [NSAttributedString attributedStringWithAttachment:textAttachment];
                    [attStr replaceCharactersInRange:subRange withAttributedString:imageStr];
                    break;
                }
            }
        }else{
            j++;
        }
    }
    return attStr;
}

本文就到此结束了。。。。。

你可能感兴趣的:(iOS NSString和NSAttributedString相互转换(更新版))