BearyChat的消息是全面支持Markdown语法的,所以在开发BearyChat的iOS客户端的时候需要处理Markdown的渲染。
主要是两套实现方案:
- 直接将Markdown文本转换成
NSAttributedString
。 - 先将Markdown文本转换成HTML,再将HTML转换成
NSAttributedString
。
方案1可用的第三方库有:AttributedMarkdown,这个库是基于C语言的peg-markdown的封装,经过试验发现对GitHub Flavored Markdown支持的不太好。
方案2可用的第三方库相对多一些:
将Markdown文本转换成HTML可用的第三方库有:MMMarkdown,GHMarkdownParser。其中GHMarkdownParser对GitHub Flavored Markdown支持比较好。
将HTML转换成NSAttributedString
,在iOS 7之后UIKit
为NSAttributedString
增加了initWithData:options:documentAttributes:error:
方法可以直接转换:1
2
3
4
5NSAttributedString * attributedString =
[[NSAttributedString alloc] initWithData:[htmlString dataUsingEncoding:NSUTF8StringEncoding]
options:@{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType,
NSCharacterEncodingDocumentAttribute: @(NSUTF8StringEncoding)}
documentAttributes:nil error:nil];
但是实测发现,这个方法的计算速度非常慢!google了一下,貌似因为这个方法渲染的过程是需要初始化ScriptCore
的,每次渲染都要初始化一个ScriptCore
肯定是不能忍的。
第三方库的替代方案:DTCoreText,NSAttributedString-DDHTML。二者之中,DTCoreText
是一个比较成熟的第三方库,对样式的控制也比较灵活。
所以最终选择的方案是:首先用GHMarkdownParser
讲Markdown转换成HTML,之后再用DTCoreText
讲HTML转换成NSAttributedString
最后交给UILabel
等控件渲染。
最终的实现代码就比较简单了:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49#import <GHMarkdownParser/GHMarkdownParser.h>
#import <DTCoreText/DTCoreText.h>
@interface MarkdownParser
@property GHMarkdownParser *htmlParser;
@property (nonatomic, copy) NSDictionary *DTCoreText_options;
- (NSAttributedString *)DTCoreText_attributedStringFromMarkdown:(NSString *)text;
@end
@implementation MarkdownParser
- (instancetype)init {
self = [super init];
if (self) {
_htmlParser = [[GHMarkdownParser alloc] init];
_htmlParser.options = kGHMarkdownAutoLink;
_htmlParser.githubFlavored = YES;
}
return self;
}
- (NSString *)htmlFromMarkdown:(NSString *)text {
return [self.htmlParser HTMLStringFromMarkdownString:text];
}
- (NSAttributedString *)attributedStringFromMarkdown:(NSString *)text {
NSString *html = [self htmlFromMarkdown:text];
NSData *data = [html dataUsingEncoding:NSUTF8StringEncoding];
NSMutableAttributedString *attributed = [[NSMutableAttributedString alloc] initWithHTMLData:data options:self.DTCoreText_options documentAttributes:nil];
return attributed;
}
- (NSDictionary *)DTCoreText_options {
if (!_DTCoreText_options) {
_DTCoreText_options = @{
DTUseiOS6Attributes:@YES,
DTIgnoreInlineStylesOption:@YES,
DTDefaultLinkDecoration:@NO,
DTDefaultLinkColor:[UIColor blueColor],
DTLinkHighlightColorAttribute:[UIColor redColor],
DTDefaultFontSize:@15,
DTDefaultFontFamily:@"Helvetica Neue",
DTDefaultFontName:@"HelveticaNeue-Light"
};
}
return _DTCoreText_options;
}
@end
到这里,绝大部分的问题都解决了,还有一点点小问题:把解析得到的NSAttributedString
丢给UILabel
的attributedString
渲染的时候,在options里设置的链接的颜色是无效的,貌似UILabel
对链接的渲染颜色是不可改的。继续寻找替代方案:用第三方的TTTAttributedLabel代替UILabel。TTTAttributedLabel
是UILabel
的派生类,为UILabel
提供了更多对NSAttributedString
的控制。通过为TTTAttributedLabel
设置超链接的样式最终解决了Markdown渲染的相关问题。
1 |
|