Swift

有使用过新浪微博会发现,微博对发送的消息提供了几个特殊符号标签。
比如:输入“@+微博用户昵称(即ID)+空格或标点”,那么在这条微博中这个@条目就会表现成超链接形式,点击即可跳转到被@的某人的微博。 
又比如话题标签“#+关键字+#”。表现形式就是点击后会跳转到包含该关键字的微博的搜索结果页面的超链接。
同样的,直接输入“http://hangge.com”那么微博中会显示为可点击的网页链接。
原文:Swift - 让textview支持特殊标签点击响应(@标签、#标签)
 
1,让textview支持特殊符号标签

通常情况下,这些带特殊标签符号内容对于多行文本框(UITextView)来说都是视为普通文本,要想让其支持点击,我们可以借助textview的URL检测功能来实现。

(1)首先遍历文本内容,将各种特殊文本替换成我们自定义的一些 URL scheme。比如:用 mention://hangge 表示 @hangge,用 hash://航歌 表示 #航歌#
(2)通过textview的 UITextFieldDelegate 代理的 shouldInteractWithURL 方法,我们可以捕获到这些自定义的 URL scheme 点击,然后通过判断 URL.scheme 来执行不同的操作。 

2,先看效果图
(1)我们在页面上放置两个 textview,下面一个用来编辑文本。
原文:Swift - 让textview支持特殊标签点击响应(@标签、#标签)

(2)点击“发送”按钮则会将内容在上面一个textview中显示出来,可以看到特殊符号的字段都显示为可点击状态。
原文:Swift - 让textview支持特殊标签点击响应(@标签、#标签)

(3)测试下各个标签的点击响应事件。
   原文:Swift - 让textview支持特殊标签点击响应(@标签、#标签)   原文:Swift - 让textview支持特殊标签点击响应(@标签、#标签)   原文:Swift - 让textview支持特殊标签点击响应(@标签、#标签)

3,实现步骤
(1)对于用来显示的textview,要将其 Detection Links(链接检测)打勾,去掉 Editable(使其不可编辑)。
原文:Swift - 让textview支持特殊标签点击响应(@标签、#标签)

(2)扩展UITextView:UITextFieldExtension.swift
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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
import UIKit
 
extension UITextView {
     
    /**
     转换特殊符号标签字段
     */
    func resolveHashTags(){
        let nsText:NSString self.text! as NSString
        // 使用默认设置的字体样式
        let attrs = [NSFontAttributeName self.font!]
        let attrString = NSMutableAttributedString(string: nsText as String,
                                                   attributes:attrs)
         
        //用来记录遍历字符串的索引位置
        var bookmark = 0
        //用于拆分的特殊符号
        let charactersSet = CharacterSet(charactersIn: "@#")
         
        //先将字符串按空格和分隔符拆分
        let sentences:[String] = self.text.components(
            separatedBy: CharacterSet.whitespacesAndNewlines)
         
        for sentence in sentences {
            //如果是url链接则跳过
            if !verifyUrl(sentence as String) {
                //再按特殊符号拆分
                let words:[String] = sentence.components(
                    separatedBy: charactersSet)
                var bookmark2 = bookmark
                for in 0..<words.count{
                    let word = words[i]
                    let keyword = chopOffNonAlphaNumericCharacters(word as String)
                    if keyword != "" && i>0{
                        //使用自定义的scheme来表示各种特殊链接,比如:mention:hangge
                        //使得这些字段会变蓝色且可点击
                         
                        //匹配的范围
                        let remainingRangeLength = min((nsText.length - bookmark2 + 1),
                                                       word.characters.count+2)
                        let remainingRange = NSRange(location: bookmark2-1,
                                                     length: remainingRangeLength)
                        print(keyword, bookmark2, remainingRangeLength)
                         
                        //获取转码后的关键字,用于url里的值
                        //(确保链接的正确性,比如url链接直接用中文就会有问题)
                        let encodeKeyword = keyword
                            .addingPercentEncoding(
                                withAllowedCharacters: CharacterSet.urlQueryAllowed)!
                         
                        //匹配@某人
                        var matchRange = nsText.range(of: "@(keyword)",
                                                options: .literal,
                                                range:remainingRange)
                        attrString.addAttribute(NSLinkAttributeName,
                                                value: "mention:(encodeKeyword)",
                                                range: matchRange)
                         
                        //匹配#话题#
                        matchRange = nsText.range(of: "#(keyword)#",
                                    options: .literal,
                                    range:remainingRange)
                        attrString.addAttribute(NSLinkAttributeName,
                                                value: "hash:(encodeKeyword)",
                                                range: matchRange)
                    }
                    //移动坐标索引记录
                    bookmark2 += word.characters.count + 1
                }
            }
             
            //移动坐标索引记录
            bookmark += sentence.characters.count + 1
        }
         
        print(nsText.length,bookmark)
         
        //最终赋值
        self.attributedText = attrString
    }
     
    /**
     验证URL格式是否正确
     */
    fileprivate func verifyUrl(_ str:String) -> Bool {
        // 创建一个正则表达式对象
        do {
            let dataDetector = try NSDataDetector(types:
                NSTextCheckingTypes(NSTextCheckingResult.CheckingType.link.rawValue))
            // 匹配字符串,返回结果集
            let res = dataDetector.matches(in: str,
                            options: NSRegularExpression.MatchingOptions(rawValue: 0),
                            range: NSMakeRange(0, str.characters.count))
            // 判断结果(完全匹配)
            if res.count == 1  && res[0].range.location == 0
                && res[0].range.length == str.characters.count {
                return true
            }
        }
        catch {
            print(error)
        }
        return false
    }
     
    /**
     过滤部多余的非数字和字符的部分
     比如:@hangge.123 -> @hangge
     */
    func chopOffNonAlphaNumericCharacters(_ text:String) -> String {
        let nonAlphaNumericCharacters = CharacterSet.alphanumerics.inverted
        let characterArray = text.components(separatedBy: nonAlphaNumericCharacters)
        return characterArray[0]
    }
}

(3)测试页面代码:
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
import UIKit
 
class ViewControllerUIViewControllerUITextViewDelegate {
 
    //展示文本框
    @IBOutlet weak var displayTextView: UITextView!
    //编辑文本框
    @IBOutlet weak var editTextView: UITextView!
     
    override func viewDidLoad() {
        super.viewDidLoad()
         
        //设置展示文本框的代理
        displayTextView.delegate = self
    }
 
    //发送消息
    @IBAction func setMessage(_ sender: AnyObject) {
        //设置展示文本框的内容
        displayTextView.text = editTextView.text
        //对内容中的特殊标签字段做处理
        displayTextView.resolveHashTags()
        //清空输入框内容
        editTextView.text = ""
    }
     
    //展示文本框链接点击响应
    func textView(_ textView: UITextView, shouldInteractWith URLURL,
                  in characterRange: NSRange) -> Bool {
        //判断URL scheme
        switch URL.scheme ?? "" {
        case "hash" :
            showAlert("hash", payload:
                (URL as NSURL).resourceSpecifier!.removingPercentEncoding!)
        case "mention" :
            showAlert("mention", payload:
                (URL as NSURL).resourceSpecifier!.removingPercentEncoding!)
        default:
            print("这个是普通的url链接")
        }
         
        return true
    }
     
    //显示消息
    func showAlert(_ tagType:String, payload:String){
        let alertController = UIAlertController(title: "检测到(tagType)标签",
            message: payload, preferredStyle: .alert)
        let cancelAction = UIAlertAction(title: "确定", style: .cancel, handler: nil)
        alertController.addAction(cancelAction)
        self.present(alertController, animated: true, completion: nil)
    }
     
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
}


原文出自:www.hangge.com  转载请保留原文链接:http://www.hangge.com/blog/cache/detail_1096.html

原文地址:https://www.cnblogs.com/-ios/p/8386484.html