iOS tableView中链接的正确处理姿势

iOS tableView中链接的正确处理姿势_第1张图片
需求是这样的

前面是我的心路历程以及踩过的坑, 赶时间可以直接从5 textView开始看

1 NSMutableAttributedString

之前公司的一个需求是要实现类似微博那样的文本中可以插入链接, 网上找了不少帖子但是发现总是不太能满足我的需求, 很多都是这样的:前面blabla说了一堆coreText的原理, 然后实现部分用NSMutableAttributedString添加attribute来实现. 然而这样写的前提是你已经知道了哪里是链接哪里是普通文字, 但实际上我们从后台拉取数据的时候是并不知道的, 所以这种方法就pass掉了.

2 Webview

tableview中有链接还算正常, 但当时的产品需求是还需要支持图文混排.
iOS tableView中链接的正确处理姿势_第2张图片
what the ?

所以后台是直接返回的html, 当时我就在琢磨, 既然这样拿webview来加载好了. 我还真就这么干了! tableview里每个cell上面是webview, 产品要的需求都实现了, 而且贼省事. 然鹅... 聪明的你可能已经预料到了, 这是一个巨大的坑. 先抛开性能问题不说, 因为是图文混排, 图片加载又是耗时操作, 所以计算cell高度是一件十分头疼的问题. 我当时又硬着头皮研究了好久的html跟js, 虽然是可以解决问题的, 但还是很坑所以这个方法还是pass了.

3 DTCoreText

再后来项目换了, 产品也离职了, 图文混排的需求没了, 不过还是需要有链接的. 组里的另外一个小伙子发现了DTCoreText的库, 这个库十分的强大, 有多强大, 看一眼库里一大堆的文件就知道有多强大了(手动滑稽). 用这个库确实能很好的解决各种跟text有关的需求但是一方面学习成本较高, 另外为这一个'小'需求引进这么大一个库总感觉不够'优雅'.

4 正则

所以我又在琢磨了, 能不能用正则, 匹配到所有的链接, 还有链接的名字, 再拿到名字的range, 就能用方法1那样直接拼attributedString了. 可能我是属于比较愚的, 没找到获取名字range的好方法, 聪明的你如果有通过这个方法实现的话欢迎留言.

5 Textview

最后终于要说我们这片文章的主角了. 废话不多说(手动滑稽)直接上代码

//记得设置代理
self.textView.delegate = self;
//赋值
self.textView.attributedText = [self attributedStringFromHTMLString:rawString];

//html string转化为attributed string
- (NSAttributedString *)attributedStringFromHTMLString:(NSString *)string{
    return [[NSAttributedString alloc] initWithData:[string dataUsingEncoding:NSUnicodeStringEncoding] options:@{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType} documentAttributes:nil error:nil];
}
//textView的代理方法
- (BOOL)textView:(UITextView *)textView shouldInteractWithURL:(NSURL *)URL inRange:(NSRange)characterRange interaction:(UITextItemInteraction)interaction{
    //对链接的处理
    return NO;
    //return YES时会跳转到Safari处理链接
    return YES;
}

是的就是这么简单! (不过中间需要注意的一点是NSAttributedString的initWithData:方法是会在主线程同步, 所以htmlString转换为attributed String不建议在cell复用的时候调用, 这样会造成tableView滑动卡顿, 我是在后台请求到数据后调用并把结果保存到model里的.)

如果你的需求没有点击cell进详情页的话这样写就足够了, 但是我们有! 因为textView需要处理点击事件来判断是否点在了链接上, 所以这样写所带来的问题就是点击非链接的普通文字时, 点击事件还是由textView处理的, 所以就不会跳转到详情页. 我琢磨了半天没找到一个完美的解决方法. 而是折中了一下:既然链接是可以拿来判断的, 那我把普通文字也当做链接处理不就好了.

//textView会给link一个默认的样式, 因为接下来要将整段文字都当链接处理, 所以应提前将textView的链接样式置为空
self.textView.linkTextAttributes = @{};
//记得设置代理
self.textView.delegate = self;
//赋值
self.textView.attributedText = [self relinkedStringFromHTMLString:rawString];

- (NSAttributedString *)relinkedStringFromHTMLString:(NSString *)rawStr linkColor:(UIColor *)linkColor textColor:(UIColor *)textColor font:(UIFont *)font{
    //从html获取初始attributed string
    NSAttributedString *attrStr = [self attributedStringFromHTMLString:rawStr];
    NSString *plainStr = attrStr.string;
    //init目标string
    NSMutableAttributedString *targetStr = [[NSMutableAttributedString alloc] initWithString:plainStr];
    NSRange wholeRange = NSMakeRange(0, plainStr.length);
    //遍历初始string获取link属性
    [attrStr enumerateAttribute:NSLinkAttributeName inRange:wholeRange options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired usingBlock:^(id  _Nullable value, NSRange range, BOOL * _Nonnull stop) {
        if (value != nil) {
            //value非空说明此range的string有连接
            [targetStr addAttribute:NSLinkAttributeName value:value range:range];
            //给链接添加一个漂亮的颜色
            [targetStr addAttribute:NSForegroundColorAttributeName value:linkColor range:range];
        } else {
            //value为空说明此range的string没有链接, 拼接一个占位URL用来判断是否为普通字符串
            [targetStr addAttribute:NSLinkAttributeName value:[NSURL URLWithString:@"text"] range:range];
            [targetStr addAttribute:NSForegroundColorAttributeName value:textColor range:range];
        }
    }];
    //设置整段string的font
    [targetStr addAttribute:NSFontAttributeName value:font range:wholeRange];
    return targetStr.copy;
}

- (BOOL)textView:(UITextView *)textView shouldInteractWithURL:(NSURL *)URL inRange:(NSRange)characterRange interaction:(UITextItemInteraction)interaction{
    if ([URL.absoluteString isEqualToString:@"text"]) {
        //普通字符串的情况, 用来处理跳转详情页等操作
        return NO;
    } else {
        //链接的情况
        return NO;
    }
    //return YES时会跳转到Safari处理链接
}

//html string转化为attributed string
- (NSAttributedString *)attributedStringFromHTMLString:(NSString *)string{
    return [[NSAttributedString alloc] initWithData:[string dataUsingEncoding:NSUnicodeStringEncoding] options:@{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType} documentAttributes:nil error:nil];
}

上面的方法解决了点击在普通文字上无法响应的问题, 不过有个缺点就是在iOS11苹果加入了UITextDraggable, 所以在长按普通文字的时候是可以将文字拖拽的(理论上讲不应该有这种操作). 目前还没找到有效的解决方法, 如果有大佬知道如何解请赐教.

以上就是我的解决方法了, 如果有帮助到你, 记得点个喜欢.
如果有任何问题或大佬有更好的解决方案欢迎留言.
转载请注明来自Xtuphe的.
谢谢

你可能感兴趣的:(iOS tableView中链接的正确处理姿势)