iOS 计算富文本,检索网址,号码,表情,并且计算高度,设置最大行数

来源:互联网 发布:淘宝鞋子女款冬天穿的 编辑:程序博客网 时间:2024/05/16 04:33

前言:项目中用到检索表情,网址与号码,但是看了TTTAttributeLabel,emojyLabel,奈何都不太满意,plist格式不太符合,而且这两个第三方用到检索都是系统自带的检索,检测网址方面不准确, 所以就需要自己使用正则进行检索。

关于以上两个三方检索不准确的可以参考:检索网址

接下来写一下实现的过程, 没有高度封装,仅供参考

关于网址与号码的正则再说明下:

网址:KURlREGULAR @"((http[s]{0,1}|ftp)://[a-zA-Z0-9\.\-]+\.([a-zA-Z]{2,4})(:\d+)?(/[a-zA-Z0-9\.\-~!@#$%^&*+?:_/=<>]*)?)|(www.[a-zA-Z0-9\.\-]+\.([a-zA-Z]{2,4})(:\d+)?(/[a-zA-Z0-9\.\-~!@#$%^&*+?:_/=<>]*)?)|(((http[s]{0,1}|ftp)://|)((?:(?:25[0-5]|2[0-4]\d|((1\d{2})|([1-9]?\d)))\.){3}(?:25[0-5]|2[0-4]\d|((1\d{2})|([1-9]?\d))))(:\d+)?(/[a-zA-Z0-9\.\-~!@#$%^&*+?:_/=<>]*)?)"

号码: KPHONENUMBERREGLAR @"\d{3}-\d{8}|\d{4}-\d{7}|\d{11}"

比如我要转的字符串为

@"简书:http://jianshu.com哈哈哈[调皮][流汗][偷笑][再见][可爱][色][害羞][委屈][委屈][抓狂][酷][酷][嘘][嘘][龇牙][大哭][大哭][大哭][龇牙][嘘][嘘][调皮][调皮]哈哈哈哈[嘘][调皮][调皮]18637963241他大舅他二舅都是舅,高桌子地板头都是木头"

我需要做的是检索网址并且替换为和微博一样的链接,号码和链接有选中状态,因为UITextview有检测url 的方法可以添加点击事件,所以就采用UITextview进行封装。主要采取正则表达式与RegexKitLite配合做检索

1 . 首先建立一个模型,把文字检索为富文本,检索出 表情,网址以及号码。中间需要使用一个模型存储检索出来的结果。对于特殊符号的表情建立一个模型。其实富文本的都是逐个检索,然后处理,最后拼接为一个NSMutableAttributedString。

#import <Foundation/Foundation.h>#import <UIKit/UIKit.h>@interface ZLStatus : NSObject//源内容@property (nonatomic, copy) NSString *text;/**    string    信息内容 -- 带有属性的(特殊文字会高亮显示\显示表情)*/@property (nonatomic, copy) NSAttributedString *attributedText;@end#import "ZLStatus.h"#import "ZLSpecial.h"#import "ZLTextPart.h"#import "RegexKitLite.h"@implementation ZLStatus- (void)setText:(NSString *)text{    _text = [text copy];    // 利用text生成attributedText    self.attributedText = [self attributedTextWithText:text];}/** *  普通文字 --> 属性文字 * *  @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 = @"((http[s]{0,1}|ftp)://[a-zA-Z0-9\\.\\-]+\\.([a-zA-Z]{2,4})(:\\d+)?(/[a-zA-Z0-9\\.\\-~!@#$%^&*+?:_/=<>]*)?)|(www.[a-zA-Z0-9\\.\\-]+\\.([a-zA-Z]{2,4})(:\\d+)?(/[a-zA-Z0-9\\.\\-~!@#$%^&*+?:_/=<>]*)?)|(((http[s]{0,1}|ftp)://|)((?:(?:25[0-5]|2[0-4]\\d|((1\\d{2})|([1-9]?\\d)))\\.){3}(?:25[0-5]|2[0-4]\\d|((1\\d{2})|([1-9]?\\d))))(:\\d+)?(/[a-zA-Z0-9\\.\\-~!@#$%^&*+?:_/=<>]*)?)";    NSString *phoneNumber =@"\\d{3}-\\d{8}|\\d{3}-\\d{7}|\\d{4}-\\d{8}|\\d{4}-\\d{7}|1+[3578]+\\d{9}|\\d{8}|\\d{7}"    ;    NSString *pattern = [NSString stringWithFormat:@"%@|%@|%@|%@|%@", emotionPattern, atPattern, topicPattern, urlPattern,phoneNumber];    // 遍历所有的特殊字符串    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;        ZLTextPart *part = [[ZLTextPart 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;        ZLTextPart *part = [[ZLTextPart alloc] init];        part.text = *capturedStrings;        part.range = *capturedRanges;        [parts addObject:part];    }];    // 排序    // 系统是按照从小 -> 大的顺序排列对象    [parts sortUsingComparator:^NSComparisonResult(ZLTextPart *part1, ZLTextPart *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];    NSString *filePath = [[NSBundle mainBundle] pathForResource:@"face.plist" ofType:nil];    NSArray  *face = [NSArray arrayWithContentsOfFile:filePath];    // 按顺序拼接每一段文字    for (ZLTextPart *part in parts) {        // 等会需要拼接的子串        NSAttributedString *substr = nil;        if (part.isEmotion) { // 表情  表情处理的时候,需要根据你自己的plist进行单独处理,像一些第三方里自定义的plist,格式要是也是很严格的            NSString *str = [text substringWithRange:part.range];            for (int i = 0; i < face.count; i ++) {                if ([face[i][@"face_name"] isEqualToString:str]) {                    //face[i][@"png"]就是我们要加载的图片                    //新建文字附件来存放我们的图片,iOS7才新加的对象                    NSTextAttachment *textAttachment = [[NSTextAttachment alloc] init];                    //给附件添加图片                    textAttachment.image = [UIImage imageNamed:face[i][@"face_image_name"]];                    //调整一下图片的位置,如果你的图片偏上或者偏下,调整一下bounds的y值即可                    textAttachment.bounds = CGRectMake(0, -6, 25, 25);                    //把附件转换成可变字符串,用于替换掉源字符串中的表情文字                   substr = [NSAttributedString attributedStringWithAttachment:textAttachment];                    break;                }            }        } else if (part.special) { // 非表情的特殊文字            NSURL *url =[NSURL URLWithString:part.text];            if (url.scheme) {                substr = [[NSAttributedString alloc] initWithString:part.text attributes:@{                                                                                           NSForegroundColorAttributeName : [UIColor redColor]                                                                                           }];               NSString *string =@"网页链接";                            NSTextAttachment *textAttachment = [[NSTextAttachment alloc] init];                                //给附件添加图片                                textAttachment.image = [UIImage imageNamed:@"链接"];                                //调整一下图片的位置,如果你的图片偏上或者偏下,调整一下bounds的y值即可                                textAttachment.bounds = CGRectMake(0, -6, 25, 25);                NSMutableAttributedString *tempAttribute = [[NSMutableAttributedString alloc]initWithAttributedString:[NSAttributedString attributedStringWithAttachment:textAttachment]];                 NSMutableAttributedString *tempAttribute2 = [[NSMutableAttributedString alloc]initWithAttributedString:[NSAttributedString attributedStringWithAttachment:textAttachment]];                NSAttributedString *text =[[NSAttributedString alloc]initWithString:string attributes:@{NSForegroundColorAttributeName:[UIColor blueColor]}];                [tempAttribute appendAttributedString:text];                substr = [[NSAttributedString alloc]initWithAttributedString:tempAttribute];                // 创建特殊对象                ZLSpecial *s = [[ZLSpecial alloc] init];                s.text = string;              //需要添加附属图片的长度,负责点击范围会变化                NSUInteger loc = attributedText.length+tempAttribute2.length;                NSUInteger len = string.length;                s.range = NSMakeRange(loc, len);                s.urlString = part.text;                [specials addObject:s];            }else{                NSLog(@"%@",part.text);                substr = [[NSAttributedString alloc] initWithString:part.text attributes:@{                                                                                           NSForegroundColorAttributeName : [UIColor redColor]                                                                                           }];                // 创建特殊对象                ZLSpecial *s = [[ZLSpecial alloc] init];                s.text = part.text;                NSUInteger loc = attributedText.length;                NSUInteger len = part.text.length;                s.range = NSMakeRange(loc, len);                s.urlString = part.text;                [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;}
中间存储检索结果与特殊字符的model:
#import <Foundation/Foundation.h>@interface ZLTextPart : NSObject/** 这段文字的内容 */@property (nonatomic, copy) NSString *text;/** 这段文字的范围 */@property (nonatomic, assign) NSRange range;/** 是否为特殊文字 */@property (nonatomic, assign, getter = isSpecical) BOOL special;/** 是否为表情 */@property (nonatomic, assign, getter = isEmotion) BOOL emotion;@end#import <Foundation/Foundation.h>@interface ZLSpecial : NSObject/** 这段特殊文字的内容 */@property (nonatomic, copy) NSString *text;/** 这段特殊文字的范围 */@property (nonatomic, assign) NSRange range;@property(nonatomic,copy)NSString *urlString;
设置完内容后我们需要计算内容高度,然后复制给Textview,建立一个Frame模型。
#import <Foundation/Foundation.h>#import "ZLStatus.h"@interface ZLFrame : NSObject//设置/** 限制最大行数  这一步必须在设置完contentLabelF之后设置*/@property(nonatomic,assign)int  maxNumLine;@property(nonatomic,strong)ZLStatus *status;@property(nonatomic,assign)CGFloat frameX;@property(nonatomic,assign)CGFloat frameY;@property(nonatomic,assign)CGFloat maxWidth;//取值/**   *//** 正文 */@property (nonatomic, assign) CGRect contentLabelF;@property(nonatomic,assign)CGRect   maxNumLabelF;@end#import "ZLFrame.h"@interface ZLFrame()//检测高度的label;@property(nonatomic,strong)UILabel *templateLabel;@end@implementation ZLFrame-(void)setStatus:(ZLStatus *)status{    _status = status;    CGSize contentSize = [status.attributedText boundingRectWithSize:CGSizeMake(self.maxWidth, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin context:nil].size;    self.contentLabelF = (CGRect){{self.frameX , self.frameY}, contentSize};}-(void)setMaxNumLine:(int)maxNumLine{    _maxNumLine = maxNumLine;    self.templateLabel.frame =CGRectMake(self.frameX, self.frameY, self.maxWidth, 0);    self.templateLabel.attributedText = self.status.attributedText;    self.templateLabel.numberOfLines = maxNumLine;    [self.templateLabel sizeToFit];    self.maxNumLabelF = self.templateLabel.frame;}-(void)setFrameX:(CGFloat)frameX{    _frameX = frameX;}-(void)setFrameY:(CGFloat)frameY{    _frameY = frameY;}-(void)setMaxWidth:(CGFloat)maxWidth{    _maxWidth = maxWidth;}-(UILabel *)templateLabel{    if (!_templateLabel) {        _templateLabel =[[UILabel alloc]init];    }    return _templateLabel;}@end




最后自定义Textview,引入Frame模型

#import <UIKit/UIKit.h>#import "ZLFrame.h"@interface ZLStatusTextView : UITextView/** 所有的特殊字符串(里面存放着HWSpecial) */@property (nonatomic, strong) NSArray *specials;@property(nonatomic,strong)ZLFrame *zlFrame;@property(nonatomic,assign)int maxLine;@property(nonatomic,assign)BOOL isShowAll;//是否全部显示@end#import "ZLStatusTextView.h"#import "ZLSpecial.h"#define ZLStatusTextViewCoverTag 999@implementation ZLStatusTextView- (id)initWithFrame:(CGRect)frame{    self = [super initWithFrame:frame];    if (self) {        self.backgroundColor = [UIColor clearColor];        self.editable = NO;        self.textContainerInset = UIEdgeInsetsMake(0, -5, 0, -5);        // 禁止滚动, 让文字完全显示出来        self.scrollEnabled = NO;    }    return self;}- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{    // 触摸对象    UITouch *touch = [touches anyObject];    // 触摸点    CGPoint point = [touch locationInView:self];    NSArray *specials = [self.attributedText attribute:@"specials" atIndex:0 effectiveRange:NULL];    BOOL contains = NO;    for (ZLSpecial *special in specials) {        self.selectedRange = special.range;        // self.selectedRange --影响--> self.selectedTextRange        // 获得选中范围的矩形框        NSArray *rects = [self selectionRectsForRange:self.selectedTextRange];        // 清空选中范围        self.selectedRange = NSMakeRange(0, 0);        for (UITextSelectionRect *selectionRect in rects) {            CGRect rect = selectionRect.rect;            if (rect.size.width == 0 || rect.size.height == 0) continue;            if (CGRectContainsPoint(rect, point)) { // 点中了某个特殊字符串                contains = YES;                break;            }        }        if (contains) {            for (UITextSelectionRect *selectionRect in rects) {                CGRect rect = selectionRect.rect;                if (rect.size.width == 0 || rect.size.height == 0) continue;                UIView *cover = [[UIView alloc] init];                cover.backgroundColor = [UIColor greenColor];                cover.frame = rect;                cover.tag = ZLStatusTextViewCoverTag;                cover.layer.cornerRadius = 5;                [self insertSubview:cover atIndex:0];            }            break;        }    }    // 在被触摸的特殊字符串后面显示一段高亮的背景}- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.25 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{        // 触摸对象        UITouch *touch = [touches anyObject];        // 触摸点        CGPoint point = [touch locationInView:self];        NSArray *specials = [self.attributedText attribute:@"specials" atIndex:0 effectiveRange:NULL];        BOOL contains = NO;        for (ZLSpecial *special in specials) {            self.selectedRange = special.range;            // self.selectedRange --影响--> self.selectedTextRange            // 获得选中范围的矩形框            NSArray *rects = [self selectionRectsForRange:self.selectedTextRange];            // 清空选中范围            self.selectedRange = NSMakeRange(0, 0);            for (UITextSelectionRect *selectionRect in rects) {                CGRect rect = selectionRect.rect;                if (rect.size.width == 0 || rect.size.height == 0) continue;                if (CGRectContainsPoint(rect, point)) { // 点中了某个特殊字符串                    contains = YES;                    break;                }            }            if (contains) {                for (UITextSelectionRect *selectionRect in rects) {                    CGRect rect = selectionRect.rect;                    if (rect.size.width == 0 || rect.size.height == 0) continue;                    if (special.urlString) {                        NSString *urlStr = special.urlString;                        NSURL *url =[NSURL URLWithString:urlStr];                        if (url.scheme) {                            [[UIApplication sharedApplication]openURL:url];                        }else{                            NSMutableString *str=[[NSMutableString alloc] initWithFormat:@"tel:%@",special.text];                            UIWebView *callWebview = [[UIWebView alloc] init];                            [callWebview loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:str]]];                            [self addSubview:callWebview];                    }                    }                }                break;            }        }        [self touchesCancelled:touches withEvent:event];    });}- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event{    // 去掉特殊字符串后面的高亮背景    for (UIView *child in self.subviews) {        if (child.tag == ZLStatusTextViewCoverTag) [child removeFromSuperview];    }}-(void)setZlFrame:(ZLFrame *)zlFrame{    _zlFrame = zlFrame;    self.attributedText = zlFrame.status.attributedText;    self.frame = zlFrame.contentLabelF;}-(void)setMaxLine:(int)maxLine{    _maxLine = maxLine;    [self.zlFrame setMaxNumLine:maxLine];    self.frame = self.zlFrame.maxNumLabelF;}-(void)setIsShowAll:(BOOL)isShowAll{    _isShowAll = isShowAll;    if (isShowAll) {        self.frame = self.zlFrame.contentLabelF;    }else{        [self setMaxLine:3];    }}@end

最后在ViewController里引入即可:

#import "ViewController.h"#import "ZLStatusTextView.h"#define kTempText  @"简书:http://jianshu.com哈哈哈[调皮][流汗][偷笑][再见][可爱][色][害羞][委屈][委屈][抓狂][酷][酷][嘘][嘘][龇牙][大哭][大哭][大哭][龇牙][嘘][嘘][调皮][调皮]哈哈哈哈[嘘][调皮][调皮]18637963241他大舅他二舅都是舅,高桌子地板头都是木头"#define  KURlREGULAR @"((http[s]{0,1}|ftp)://[a-zA-Z0-9\\.\\-]+\\.([a-zA-Z]{2,4})(:\\d+)?(/[a-zA-Z0-9\\.\\-~!@#$%^&*+?:_/=<>]*)?)|(www.[a-zA-Z0-9\\.\\-]+\\.([a-zA-Z]{2,4})(:\\d+)?(/[a-zA-Z0-9\\.\\-~!@#$%^&*+?:_/=<>]*)?)|(((http[s]{0,1}|ftp)://|)((?:(?:25[0-5]|2[0-4]\\d|((1\\d{2})|([1-9]?\\d)))\\.){3}(?:25[0-5]|2[0-4]\\d|((1\\d{2})|([1-9]?\\d))))(:\\d+)?(/[a-zA-Z0-9\\.\\-~!@#$%^&*+?:_/=<>]*)?)"#define KPHONENUMBERREGLAR @"\\d{3}-\\d{8}|\\d{3}-\\d{7}|\\d{4}-\\d{8}|\\d{4}-\\d{7}|1+[3578]+\\d{9}|\\d{8}|\\d{7}"#import "ZLStatus.h"#import "ZLFrame.h"#import "LxButton.h"@interface ViewController ()<UITextViewDelegate>@property(nonatomic,strong)ZLStatusTextView *textview;@end@implementation ViewController- (void)viewDidLoad {    [super viewDidLoad];    // Do any additional setup after loading the view, typically from a nib.    self.textview =[[ZLStatusTextView alloc]initWithFrame:CGRectMake(20, 100, 250, 200)];    ZLStatus *status = [[ZLStatus alloc]init];    status.text = kTempText;    ZLFrame *zlFrame =[[ZLFrame alloc]init];    zlFrame.frameX = self.textview.frame.origin.x;    zlFrame.frameY = self.textview.frame.origin.y;    zlFrame.maxWidth = self.textview.frame.size.width;    zlFrame.status = status;    self.textview.zlFrame = zlFrame;    //设置最大行数用于展开    self.textview.maxLine = 3;    self.textview.isShowAll = YES;    [self.view addSubview:self.textview];    self.textview.backgroundColor =[UIColor lightGrayColor];    LxButton *button =[LxButton LXButtonWithTitle:@"限制最大行数" titleFont:[UIFont systemFontOfSize:15] Image:nil backgroundImage:nil backgroundColor:[UIColor brownColor] titleColor:[UIColor blueColor] frame:CGRectMake(20, 40, 150, 40)];    [self.view addSubview:button];    __weak ViewController *weakSelf = self;    [button addClickBlock:^(UIButton *button) {        weakSelf.textview.isShowAll =!weakSelf.textview.isShowAll;    }];}@end

demo地址:富文本检索表情,网址,替换链接,限制最大输入行


链接:http://www.jianshu.com/p/77c3b27f8858

阅读全文
0 0
原创粉丝点击