iOS開發中的Markdown渲染


iOS開發中的Markdown渲染

BearyChat的消息是全面支持Markdown語法的,所以在開發BearyChat的iOS客戶端的時候需要處理Markdown的渲染。

主要是兩套實現方案:

  1. 直接將Markdown文本轉換成NSAttributedString
  2. 先將Markdown文本轉換成HTML,再將HTML轉換成NSAttributedString

方案1可用的第三方庫有:AttributedMarkdown,這個庫是基於C語言的peg-markdown的封裝,經過試驗發現對GitHub Flavored Markdown支持的不太好。

方案2可用的第三方庫相對多一些:

將Markdown文本轉換成HTML可用的第三方庫有:MMMarkdownGHMarkdownParser。其中GHMarkdownParserGitHub Flavored Markdown支持比較好。

將HTML轉換成NSAttributedString,在iOS 7之后UIKitNSAttributedString增加了initWithData:options:documentAttributes:error:方法可以直接轉換:









但是實測發現,這個方法的計算速度非常慢!google了一下,貌似因為這個方法渲染的過程是需要初始化ScriptCore的,每次渲染都要初始化一個ScriptCore肯定是不能忍的。
第三方庫的替代方案:DTCoreTextNSAttributedString-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丟給UILabelattributedString渲染的時候,在options里設置的鏈接的顏色是無效的,貌似UILabel對鏈接的渲染顏色是不可改的。繼續尋找替代方案:用第三方的TTTAttributedLabel代替UILabel。TTTAttributedLabelUILabel的派生類,為UILabel提供了更多對NSAttributedString的控制。通過為TTTAttributedLabel設置超鏈接的樣式最終解決了Markdown渲染的相關問題。

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
50
51
52
53
54
55
56
57
58

#import <UIKit/UIKit.h>
#import <TTTAttributedLabel/TTTAttributedLabel.h>

@interface MarkdownLabel : TTTAttributedLabel <TTTAttributedLabelDelegate>
- (void)setDisplayedAttributedString:(id)text;
@end

@implementation MarkdownLabel

- (instancetype)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if (self) {
[self commonConfig];
}
return self;
}

- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self commonConfig];
}
return self;
}

- (void)commonConfig {
self.delegate = self;
NSDictionary *linkAttributes = @{
(id)kCTForegroundColorAttributeName:[UIColor blueColor],
NSUnderlineStyleAttributeName:@(kCTUnderlineStyleNone),
NSFontAttributeName:[UIFont fontWithName:@"HelveticaNeue-Light" size:15]
};
self.linkAttributes = linkAttributes;
self.enabledTextCheckingTypes = 0;
}

- (void)setDisplayedAttributedString:(id)text {
NSMutableArray *linksAndRange = [@[] mutableCopy];
[self setText:[text string] afterInheritingLabelAttributesAndConfiguringWithBlock:^NSMutableAttributedString *(NSMutableAttributedString *mutableAttributedString) {
[text enumerateAttributesInRange:NSMakeRange(0, [text length])
options:0
usingBlock:^(NSDictionary *attrs, NSRange range, BOOL *stop) {
if (attrs[NSLinkAttributeName]) {
[linksAndRange addObject:@[attrs[NSLinkAttributeName], [NSValue valueWithRange:range]]];
} else {
[mutableAttributedString addAttributes:attrs range:range];
}
}];
return mutableAttributedString;
}];

for (NSArray *pair in linksAndRange) {
[self addLinkToURL:pair[0] withRange:[pair[1] rangeValue]];
}
}

@end

 

http://nightfade.github.io/2015/06/26/ios-markdown-rendering/

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM