iOS 计算富文本,检索网址,号码,表情,并且计算高度,设置最大行数
2017-07-21 09:22
471 查看
前言:项目中用到检索表情,网址与号码,但是看了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 4000 }|\\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" #impor adf6 t "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
相关文章推荐
- iOS 计算富文本,检索网址,号码,表情,并且计算高度,设置最大行数(聊天表情计算)
- iOS 计算富文本高度和设置段落样式
- IOS计算UILabel单行高度、计算UILabel多行文本高度、宽度的技巧,根据文本的高度设置cell高度
- IOS 富文本 ,设置行间距、字间距,计算高度(转载组合而成)
- iOS-设置文本行高并计算高度
- iOS-[NSAttributedString]设置富文本和计算富文本高度
- IOS core text计算文本高度及最大宽度
- iOS中计算文本高度
- iOS计算文本高度
- 【iOS开发】---- iOS6 以上设置文本高度,行高
- iOS开发动态计算cell高度或UILabel多行文本计算高度、宽度的技巧
- 【iOS】UILabel多行文本的高度计算
- iOS学习笔记 自定义cell并且文本自适应高度
- UITextView自适应高度,同时可以设置最大高度, 类似于QQ微信输入框效果,支持表情的输入
- ios中根据文本设置label高度
- iOS UILabel设置行间距和字间距并计算高度
- iOS UILabel 设置内容的间距及高度的计算示例
- iOS UILabel设置行间距和字间距并计算高度
- iOS UILabel 设置 行间距 和 字间距 并计算高度
- textVIew 在输入时 设置行数并且自适应高度 上移动