zoukankan      html  css  js  c++  java
  • 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/

  • 相关阅读:
    Windows Azure Web Site (19) Azure Web App链接到VSTS
    Windows Azure Virtual Machine (35) Azure VM通过Linked DB,执行SQL Job
    Azure PowerShell (16) 并行开关机Azure ARM VM
    Windows Azure Virtual Network (12) 虚拟网络之间点对点连接VNet Peering
    Azure ARM (21) Azure订阅的两种管理模式
    Windows Azure Platform Introduction (14) 申请海外的Windows Azure账户
    Azure ARM (20) 将非托管磁盘虚拟机(Unmanage Disk),迁移成托管磁盘虚拟机(Manage Disk)
    Azure ARM (19) 将传统的ASM VM迁移到ARM VM (2)
    Azure ARM (18) 将传统的ASM VM迁移到ARM VM (1)
    Azure Automation (6) 执行Azure SQL Job
  • 原文地址:https://www.cnblogs.com/feng9exe/p/10321109.html
Copyright © 2011-2022 走看看