我是靠谱客的博主 虚心方盒,这篇文章主要介绍 可点击 @、# 标记文本实现,现在分享给大家,希望可以做个参考。

在社交类 APP 中 @、# 符号构成的标记文本已经形成了某种通用的意义:前者表示通知某位好友,而后者表示为某个话题或者分类。这些标记文本一般还都带有高亮显示和可点击的特点。接下来的我会创建一个 UITextView 的子类 AttrTextView 来实现上诉功能。

开始

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import UIKit enum wordType{ case hashtag // #标示文本类型 case mention // @标示文本类型 } //自定义视图用于高亮 # 和 @ 之后的文本(效果类似于微博、twitter),并添加点击事件 class AttrTextView: UITextView { var textString: NSString? var attrString: NSMutableAttributedString? var callBack: ((String, wordType) -> Void)? ... }

上码的代码首先声明了一个 wordType 的枚举类型,该类用用于对标示文本进行类型标记。接着我们定义了自定义类型 AttrTextView,并且声明了三个属性。textString 表示原文本,attrString 进行属性设置后的文本,callBack 为 # 和 @ 标记文本的点击事件回调。

文本设置

定义好属性后,我们就需要考虑使用接口的实现了。正常情况下文本应该有以下属性需要设置:常规文本的字体、颜色;# 和 @ 标记文本各自对应的字体和颜色;点击事件设置以及回调函数。代码如下:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public func setText(text: String,normalColor: UIColor, hashtagColor: UIColor, mentionColor: UIColor, normalFont: UIFont, hashTagFont: UIFont, mentionFont: UIFont,tapCallBack callBack: @escaping (String, wordType) -> Void) { self.callBack = callBack self.attrString = NSMutableAttributedString(string: text) self.textString = NSString(string: text) // Set initial font attributes for our string // 设置字体和文本颜色 attrString?.addAttribute(NSFontAttributeName, value: normalFont, range: NSRange(location: 0, length: (textString?.length)!)) attrString?.addAttribute(NSForegroundColorAttributeName, value: normalColor, range: NSRange(location: 0, length: (textString?.length)!)) // Call a custom set Hashtag and Mention Attributes Function // 设置 #、@ 的高亮色等属性 setAttrWithName(attrName: "Hashtag", wordPrefix: "#", color: hashtagColor, text: text, font: hashTagFont) setAttrWithName(attrName: "Mention", wordPrefix: "@", color: mentionColor, text: text, font: mentionFont) // Add tap gesture that calls a function tapRecognized when tapped // 添加手势 let tapper = UITapGestureRecognizer(target: self, action: #selector(self.tapRecognized(tapGesture:))) addGestureRecognizer(tapper) }

上面代码中的 setAttrWithName 函数的目的是对 #、@ 标记文本的属性进行设置,代码如下:

复制代码
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
private func setAttrWithName(attrName: String, wordPrefix: String, color: UIColor, text: String, font: UIFont) { // Words can be separated by either a space or a line break // 将文本按照空格和 n 键拆分为单词数组 var words: [String] = [] let wordtext: [String] = text.components(separatedBy: " ") for var word in wordtext { if word.hasPrefix("n") { word = word.replacingOccurrences(of: "n", with: "") } words.append(word) } // 便利数组,检查是否满足条件并进行属性设置 for word in words.filter({$0.hasPrefix(wordPrefix)}) { let range = textString!.range(of: word) attrString?.addAttribute(NSForegroundColorAttributeName, value: color, range: range) attrString?.addAttribute(attrName, value: 1, range: range) attrString?.addAttribute("Clickable", value: 1, range: range) attrString?.addAttribute(NSFontAttributeName, value: font, range: range) } self.attributedText = attrString }

点击事件的处理

文本点击的处理稍微有点麻烦,需要考虑多种情况:

  • 没有点击在任何文本上

  • 点击在普通文本

  • 点击在标示文本,并且需要识别标示文本的类型

复制代码
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
func tapRecognized(tapGesture: UITapGestureRecognizer) { var wordString: String? // The String value of the word to pass into callback function var char: NSAttributedString! //The character the user clicks on. It is non optional because if the user clicks on nothing, char will be a space or " " var word: NSAttributedString? //The word the user clicks on var isHashtag: AnyObject? var isAtMention: AnyObject? // Gets the range of the character at the place the user taps // 检查用户点击字符的范围 let point = tapGesture.location(in: self) let charPosition = closestPosition(to: point) guard let charRange = tokenizer.rangeEnclosingPosition(charPosition!, with: .character, inDirection: 1) else { return } let location = offset(from: beginningOfDocument, to: charRange.start) let length = offset(from: charRange.start, to: charRange.end) let attrRange = NSMakeRange(location, length) char = attributedText.attributedSubstring(from: attrRange) // If the user has not clicked on anything, exit the function if char.string == " "{ print("User clicked on nothing") return } // Checks the character's attribute, if any // 检查属性标示 isHashtag = char?.attribute("Hashtag", at: 0, longestEffectiveRange: nil, in: NSMakeRange(0, char!.length)) as AnyObject? isAtMention = char?.attribute("Mention", at: 0, longestEffectiveRange: nil, in: NSMakeRange(0, char!.length)) as AnyObject? // Gets the range of the word at the place user taps // 获得点击单词的范围 let wordRange = tokenizer.rangeEnclosingPosition(charPosition!, with: .word, inDirection: 1) /* 单词的范围在下面两种情况下为 nil: 1. 点击在 "#" or "@" 标示上 2. 没有点击在任何字符上。但是这种情况在上面的代码中已经排除了,所有只剩下 1 */ if wordRange != nil { let wordLocation = offset(from: beginningOfDocument, to: wordRange!.start) let wordLength = offset(from: wordRange!.start, to: wordRange!.end) let wordAttrRange = NSMakeRange(wordLocation, wordLength) word = attributedText.attributedSubstring(from: wordAttrRange) wordString = word!.string } else { /* 右移12像素后再获取单词 */ var modifiedPoint = point modifiedPoint.x += 12 let modifiedPosition = closestPosition(to: modifiedPoint) let modifedWordRange = tokenizer.rangeEnclosingPosition(modifiedPosition!, with: .word, inDirection: 1) if modifedWordRange != nil { let wordLocation = offset(from: beginningOfDocument, to: modifedWordRange!.start) let wordLength = offset(from: modifedWordRange!.start, to: modifedWordRange!.end) let wordAttrRange = NSMakeRange(wordLocation, wordLength) word = attributedText.attributedSubstring(from: wordAttrRange) wordString = word!.string } } if let stringToPass = wordString { // 点击回掉函数 if isHashtag != nil && callBack != nil { callBack!(stringToPass, wordType.hashtag) } else if isAtMention != nil && callBack != nil { callBack!(stringToPass, wordType.mention) } } }

上面的代码处理中,首先使用 .character 检查点击位置的字符,并对无效区域的点击进行了处理。这里之所以使用 .character 而不是后面的 .word 的原因是:后者会将 @、# 这些标示符丢弃,导致一只类似点击到无效区域的情形。当上诉检查通过也就是点击区域有效的时候,我们使用 .word,获取点击区域的单词。为了应对前面标示点击的情形,当区域无效的时候,我们右移12个像素后再获取单词。最后我们根据文本不同类型进行对应处理。

最后

最后我们看一下简单使用示例代码:

复制代码
1
2
3
4
5
6
7
let attrView = AttrTextView.init(frame: CGRect.init(x: 0, y: 64, width: view.bounds.size.width, height: view.bounds.size.height - 64), textContainer: nil) self.view.addSubview(attrView) attrView.setText(text: "#PHP 是不是世界上最好的语言? @all ",normalColor: .black, hashtagColor: .red, mentionColor: .blue, normalFont: UIFont.systemFont(ofSize: 10), hashTagFont: UIFont.systemFont(ofSize: 14), mentionFont: UIFont.systemFont(ofSize: 14)) { word,wordType in print(word) }

最后

以上就是虚心方盒最近收集整理的关于 可点击 @、# 标记文本实现的全部内容,更多相关内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(51)

评论列表共有 0 条评论

立即
投稿
返回
顶部