ASCII码 ASCII码

iOS UITextView 实现类似微博的话题、提及用户效果

发布于:2022-06-29 20:17:22  栏目:技术文档

最近接了一个需求,在发布动态的时候,增加类似微博的#话题#、@提及用户的效果,在此做一简要记录。

话题

最终效果是:

编辑过程中#话题内容#实时高亮

高亮部分可以响应点击事件

1.高亮基本思路是:使用正则匹配出成对的#,再利用UITextView的富文本实现高亮效果。 func refreshTopicStyle() {

  1. let regex = try! NSRegularExpression(pattern: "此处填写正则表达式",
  2. options:[NSRegularExpression.Options.caseInsensitive])
  3. // 注意点
  4. let totalRange = NSMakeRange(0, (inputTextView.attributedText.string as NSString).length)
  5. let results = regex.matches(in: inputTextView.attributedText.string,
  6. options: NSRegularExpression.MatchingOptions.init(rawValue: 0),
  7. range: totalRange)
  8. let attributedString: NSMutableAttributedString = NSMutableAttributedString(string: inputTextView.attributedText.string)
  9. attributedString.setAttributes(normalAttributes, range: totalRange)
  10. for result in results {
  11. attributedString.setAttributes(topicAttributes, range: result.range)
  12. }
  13. inputTextView.attributedText = attributedString
  14. }

这有一个注意点,计算 totalRange 前,先将 String 转成了 NSString,这是因为此处 NSRange 中的 length 需要的是 UTF-16 长度,也就是与 NSString 的 length 定义一致,而 Swift 中的 String 没有 length 只有 count,指的是字符数,当文本中出现 emoji 表情时,二者就不一致了。当然,也有一些其他办法来处理,如: let lengthA = inputTextView.textStorage.length let lengthB = inputTextView.attributedText.string.utf16.count2.点击事件实现高亮部分的点击事件,目前有3种实现方案:

直接给UITextView添加点击事件通过设置LinkAttribute,利用超文本链接的点击实现重写UITextView的touches…方法其中,第二种只限于在非编辑状态(即 textView.isEditable = false)下的点击,故排除,①、③均可,本文采用第一种,主要实现如下: inputTextView.addTapGesture(self, handler: #selector(tapAttributedText(tap:))) @objc private func tapAttributedText(tap: UITapGestureRecognizer) { guard tap.isKind(of: UITapGestureRecognizer.self), let textView = tap.view as? UITextView else { return } let layoutManager = textView.layoutManager var tapLocation = tap.location(in: textView) tapLocation.x -= textView.textContainerInset.left tapLocation.y -= textView.textContainerInset.top let characterIndex = layoutManager.characterIndex(for: tapLocation, in: textView.textContainer, fractionOfDistanceBetweenInsertionPoints: nil) for result in getCheckResult(format: Constants.TopicRegularExpression, text: inputTextView.attributedText.string) { if result.range.location < characterIndex, characterIndex < result.range.location + result.range.length { // 此处响应点击事件 MBProgressHUD.showOnlyText(to: self.view, title: “美好时光”) return } } inputTextView.becomeFirstResponder() }``

相关推荐
阅读 +