可点击 @、# 标记文本实现

前端之家收集整理的这篇文章主要介绍了可点击 @、# 标记文本实现前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

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

开始

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

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

文本设置

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

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

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

  1. private func setAttrWithName(attrName: String,wordPrefix: String,color: UIColor,text: String,font: UIFont) {
  2. // Words can be separated by either a space or a line break
  3. // 将文本按照空格和 \n 键拆分为单词数组
  4. var words: [String] = []
  5. let wordtext: [String] = text.components(separatedBy: " ")
  6. for var word in wordtext {
  7. if word.hasPrefix("\n") {
  8. word = word.replacingOccurrences(of: "\n",with: "")
  9. }
  10. words.append(word)
  11. }
  12. // 便利数组,检查是否满足条件并进行属性设置
  13. for word in words.filter({$0.hasPrefix(wordPrefix)}) {
  14. let range = textString!.range(of: word)
  15. attrString?.addAttribute(NSForegroundColorAttributeName,value: color,range: range)
  16. attrString?.addAttribute(attrName,value: 1,range: range)
  17. attrString?.addAttribute("Clickable",range: range)
  18. attrString?.addAttribute(NSFontAttributeName,value: font,range: range)
  19. }
  20. self.attributedText = attrString
  21. }

点击事件的处理

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

  • 没有点击在任何文本上

  • 点击在普通文本

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

  1. func tapRecognized(tapGesture: UITapGestureRecognizer) {
  2. var wordString: String? // The String value of the word to pass into callback function
  3. 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 " "
  4. var word: NSAttributedString? //The word the user clicks on
  5. var isHashtag: AnyObject?
  6. var isAtMention: AnyObject?
  7. // Gets the range of the character at the place the user taps
  8. // 检查用户点击字符的范围
  9. let point = tapGesture.location(in: self)
  10. let charPosition = closestPosition(to: point)
  11. guard let charRange = tokenizer.rangeEnclosingPosition(charPosition!,with: .character,inDirection: 1) else {
  12. return
  13. }
  14. let location = offset(from: beginningOfDocument,to: charRange.start)
  15. let length = offset(from: charRange.start,to: charRange.end)
  16. let attrRange = NSMakeRange(location,length)
  17. char = attributedText.attributedSubstring(from: attrRange)
  18. // If the user has not clicked on anything,exit the function
  19. if char.string == " "{
  20. print("User clicked on nothing")
  21. return
  22. }
  23. // Checks the character's attribute,if any
  24. // 检查属性标示
  25. isHashtag = char?.attribute("Hashtag",at: 0,longestEffectiveRange: nil,in: NSMakeRange(0,char!.length)) as AnyObject?
  26. isAtMention = char?.attribute("Mention",char!.length)) as AnyObject?
  27. // Gets the range of the word at the place user taps
  28. // 获得点击单词的范围
  29. let wordRange = tokenizer.rangeEnclosingPosition(charPosition!,with: .word,inDirection: 1)
  30. /*
  31. 单词的范围在下面两种情况下为 nil:
  32. 1. 点击在 "#" or "@" 标示上
  33. 2. 没有点击在任何字符上。但是这种情况在上面的代码中已经排除了,所有只剩下 1
  34. */
  35. if wordRange != nil {
  36. let wordLocation = offset(from: beginningOfDocument,to: wordRange!.start)
  37. let wordLength = offset(from: wordRange!.start,to: wordRange!.end)
  38. let wordAttrRange = NSMakeRange(wordLocation,wordLength)
  39. word = attributedText.attributedSubstring(from: wordAttrRange)
  40. wordString = word!.string
  41. } else {
  42. /*
  43. 右移12像素后再获取单词
  44. */
  45. var modifiedPoint = point
  46. modifiedPoint.x += 12
  47. let modifiedPosition = closestPosition(to: modifiedPoint)
  48. let modifedWordRange = tokenizer.rangeEnclosingPosition(modifiedPosition!,inDirection: 1)
  49. if modifedWordRange != nil {
  50. let wordLocation = offset(from: beginningOfDocument,to: modifedWordRange!.start)
  51. let wordLength = offset(from: modifedWordRange!.start,to: modifedWordRange!.end)
  52. let wordAttrRange = NSMakeRange(wordLocation,wordLength)
  53. word = attributedText.attributedSubstring(from: wordAttrRange)
  54. wordString = word!.string
  55. }
  56. }
  57. if let stringToPass = wordString {
  58. // 点击回掉函数
  59. if isHashtag != nil && callBack != nil {
  60. callBack!(stringToPass,wordType.hashtag)
  61. } else if isAtMention != nil && callBack != nil {
  62. callBack!(stringToPass,wordType.mention)
  63. }
  64. }
  65. }

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

最后

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

  1. let attrView = AttrTextView.init(frame: CGRect.init(x: 0,y: 64,width: view.bounds.size.width,height: view.bounds.size.height - 64),textContainer: nil)
  2. self.view.addSubview(attrView)
  3. 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
  4. print(word)
  5. }

猜你在找的Swift相关文章