需求来源
从后台返回的数据是一个数组,每个数组元素是html字符串,可以显示在一个UIWebView中。
在显示这个html字符串的详情页面的上一级是一个列表,展示标题和内容简介。但是,后台返回的数据中并没有简介字段。
万能的交互和技术一商量,接口不改,让iOS和Android客户端从“html字符串”中抽出具体的内容,当成简介。列表就显示3行,多余的用...来表示。霸道而虚弱的后台,就算吐槽也没用。
方案一:NSAttributedString
项目最低支持iOS7,所以NSAttributedString可以使用,其中有一个option叫做NSHTMLTextDocumentType,就是专门用来从“html字符串”抽取纯文本的。iOS UILabel显示HTML文本
这种API比较难用,所以这里要记一下,希望苹果以后能提供好用一点的API
NSString *htmlString = @"<html><body> Some html string \n <font size=\"13\" color=\"red\">This is some text!</font> </body></html>";
NSAttributedString *attrStr = [[NSAttributedString alloc] initWithData:[htmlString dataUsingEncoding:NSUnicodeStringEncoding] options:@{ NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType } documentAttributes:nil error:nil];
UILabel *myLabel = [[UILabel alloc] initWithFrame:self.view.bounds];
myLabel.attributedText = attrStr;
[self.view addSubview:myLabel];
从功能上来说,没有问题。不过,我们的应用场景是表格,而NSAttributedString是有比较严重的性能问题,表格会很卡,甚至还会崩溃。NSHTMLTextDocumentType is Slow
方案二:NSString+HTML
谷歌在很久之前就出过一个toolbox,里面有从“html字符串”抽取内容的函数。google-toolbox-for-mac这个工具箱内容很多,这里只要用GTMNSString+HTML这个类别中的函数- (NSString *)gtm_stringByEscapingForHTML;
就可以了。
实际用了一下,性能问题是解决了,不过有两个障碍需要解决:
- 这个库比较老,而且还是MRC的
- 大多数的html的标签是可以去掉的,但是还有残留,有些html标签去不掉,功能有问题,具体的原因,暂时还不清楚。
怎么解决
现在的工程是ARC的,引入MRC的源文件,会编译不过。所以要加编译选项,进行ARC和MRC的混编。对于需要MRC的源文件,添加-fno-objc-arc标记。Xcode中实现ARC和MRC混编
对于标签残留的问题,已经有人经过深入的研究,并且给出了解决方法GTMNSString-HTML用这里的NSString+HTML类别中的
- (NSString *)stringByConvertingHTMLToPlainText;
函数就可以了。
发现的坑
htmlUnescapes
是一个全局的字典,通过键值对的方式将html的一些特殊标签替换为本地的表示方式。比如,将<br>
替换为\n
;但是这里却忘了将<br/>
替换为\n
;- (NSString *)stringByConvertingHTMLToPlainText;
函数实现的扫描比对部分还是有问题的。比如输入是“abcd(abc), 1821”,结果变成了“abcd(abc), ”,数字“1821”被替换成了“”空字符。具体的原因和解决方法还没有找到。这个第三方库的star数目有二百多,是在gitHub上输入HTML和Object-C关键字出现的star数目最多的一个。不过最后的更新还是在4年前,Goggle那个工具箱也是老掉牙了,还是MRC的。只是现在没有找到更好的,暂时还能用。
方案三:自己写
- 如果是将html中的<>标签全部去除,那么实现就相对简单,可以自己实现。<>标签没有嵌套,这是很重要的一个特性。将<>标签里的内容,包括<>本身都替换为空字符串“”就可以了。IOS去掉字符串中HTML标签的方法
- (NSString *)filterHTML:(NSString *)html {
NSScanner *scanner = [NSScanner scannerWithString:html];
NSString *text = nil;
while(![scanner isAtEnd]) {
//找到标签的起始位置
[scanner scanUpToString:@"<" intoString:nil];
//找到标签的结束位置
[scanner scanUpToString:@">" intoString:&text];
//替换字符
html = [html stringByReplacingOccurrencesOfString:[NSString stringWithFormat:@"%@>",text] withString:@""];
}
return html;
}
如果需求复杂一点,比如
<br>
,<br/>
要保留,不能简单的去掉,而是要替换为\n
那么实现就复杂一点了。估计要在上面的代码中加入额外的判断了如果要考虑全html那些特殊标签,那么估计代码就更复杂一点了。上面用的第三方库,基本的思路也是这个,考虑得比较全面,代码比较多。