美文网首页iOS学习开发
iOS tableView中链接的正确处理姿势

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

作者: Xtuphe | 来源:发表于2018-04-07 21:29 被阅读15次
    需求是这样的

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

    1 NSMutableAttributedString

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

    2 Webview

    tableview中有链接还算正常, 但当时的产品需求是还需要支持图文混排. 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中链接的正确处理姿势

        本文链接:https://www.haomeiwen.com/subject/wsxchftx.html