Swift学习iOS进阶之路

iOS开发实战 - 精确匹配文本中的链接

2020-07-10  本文已影响0人  ArchLL

日常开发中经常会遇到对文本中链接匹配不准确的问题,让人头痛不已🐶
比如:
1.链接中包含中文字符;
2.链接尾部如果紧跟中文字符的话,也会被当作链接的一部分匹配进去;

以上这些情况都会导致点击链接无效,降低用户的体验,
于是参考微信的匹配链接效果做了一些优化。

错误示例:

错误示例1 - 链接内部包含中文字符 错误示例2 - 链接尾部紧跟中文字符

正确示例:

正确示例1 - 包含中文字符的链接不匹配 正确示例2 - 末尾的中文字符不匹配 正确示例3 - 实现精准匹配

代码示例:

语言swift 5.0
依赖YYLabel
拓展:本文提供的思路完全可以应用在UITextViewUIFieldView以及其他一些第三方的文本组件中

import UIKit

class YYLinkLabel: YYLabel {
    
    override var textColor: UIColor! {
        didSet {
            // 切换黑夜模式的时候需要去重新match link,否则link显示的颜色不正确
            matchLink()
        }
    }
    
    /// 匹配link
    func matchLink() {
        guard let text = attributedText, text.length > 0 else {
            return
        }
        
        let regularRange = NSRange(location: 0, length: text.length)
        guard let detector = try? NSDataDetector(types: NSTextCheckingResult.CheckingType.link.rawValue) else {
            return
        }
        
        detector.enumerateMatches(in: text.string, options: [], range: regularRange) { (match, falgs, nil) in
            // ⚠️ 如果原始链接中不包含scheme,如:`https://`,则转换成url后会自动加上前缀`http://`
            guard let match = match, let url = match.url else {
                return
            }
            
            // 对url进行解码
            let urlString = url.absoluteString.removingPercentEncoding ?? ""
            // 获取链接末尾中文字符的数量
            let number = urlString.numberOfFootChineseCharactersCount()
            // 获取已经移除链接末尾的中文字符的字符串
            let realUrlString = urlString.stringOfRemoveFootChineseCharacters(number: number)
            
            guard !realUrlString.isContainsChineseCharacters() else {
                // 如果链接中含有中文字符,意味着这个链接无效,故不对其设置高亮
                return
            }
            
            // ⚠️ 这里不能拿urlString/realUrlString去计算range,因为urlString可能已经加了scheme
            let range = NSRange(location: match.range.location, length: match.range.length - number)
            
            // 对匹配到的内容设置高亮
            (text as? NSMutableAttributedString)?.yy_setTextHighlight(range, color: UIColor.blue(), backgroundColor: .clear, tapAction: { (containerView, text, range, rect) in
                HGPortal.transfer(from: nil, toURL: realUrlString)
            })
        }
    }
}

private extension String {
    /// 是否包含中文字符(主要是为了判断链接中间是否包含中文字符)
    func isContainsChineseCharacters() -> Bool {
        var result = false
        for (_, c) in enumerated() {
            if isChineseCharacter(c) {
                result = true
                break
            }
        }
        return result
    }
    
    /// 获取末尾中文字符的个数
    func numberOfFootChineseCharactersCount() -> Int {
        var number = 0
        for (i, c) in reversed().enumerated() {
            if isChineseCharacter(c) {
                number = i + 1
            } else {
                break
            }
        }
        return number
    }
    
    /// 得到一个已经去除末尾的中文字符的字符串
    func stringOfRemoveFootChineseCharacters(number: Int) -> String {
        guard number > 0 else {
            return self
        }
        return String(prefix(utf16.count - number))
    }
    
    /// 判断是否是中文字符
    private func isChineseCharacter(_ character: Character) -> Bool {
        if "\u{4E00}" <= character && character <= "\u{9FA5}" {
            return true
        }
        return false
    }
}

😊 创作不易,转载请注明出处

上一篇 下一篇

猜你喜欢

热点阅读