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

 更新时间:2022年06月07日 11:27:07   作者:XboxYan  
这篇文章主要介绍了iOS UITextView 实现类似微博的话题、提及功能,基本思路是使用正则匹配出成对的#,再利用UITextView的富文本实现高亮效果,需要的朋友可以参考下

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

#话题#

最终效果是:

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

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

1.高亮

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

func refreshTopicStyle() {
        
        let regex = try! NSRegularExpression(pattern: "此处填写正则表达式",
                                             options:[NSRegularExpression.Options.caseInsensitive])
        // 注意点
        let totalRange = NSMakeRange(0, (inputTextView.attributedText.string as NSString).length)
        let results = regex.matches(in: inputTextView.attributedText.string,
                                    options: NSRegularExpression.MatchingOptions.init(rawValue: 0),
                                    range: totalRange)
        let attributedString: NSMutableAttributedString = NSMutableAttributedString(string: inputTextView.attributedText.string)
        attributedString.setAttributes(normalAttributes, range: totalRange)
        
        for result in results {
            attributedString.setAttributes(topicAttributes, range: result.range)
        }
        inputTextView.attributedText = attributedString
    }

这有一个注意点,计算 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.count

2.点击事件

实现高亮部分的点击事件,目前有3种实现方案:

  • 直接给UITextView添加点击事件
  • 通过设置LinkAttribute,利用超文本链接的点击实现
  • 重写UITextViewtouches...方法

其中,第二种只限于在非编辑状态(即 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()
}

@提及用户

  • 编辑过程中 @提及用户 实时高亮,且只允许选取的用户名高亮,手动输入不高亮;

  • 点击删除键的时候,一次性删除整个高亮部分

1.高亮

  • 记录位置

本来准备用正则匹配的,但因为只允许选取的用户名高亮,纯手动输入的不高亮,所以使用正则匹配就不合理了,这里采用实时记录、更新已选取用户名位置的方式实现。

/// 用来保存已选取用户信息的结构体
struct UserInfo {
    /// 用户名
    var userName: String
    /// 位置信息
    var range: NSRange
    /// 用于临时替换的等长字符串
    var placeholder: String
}
  • 临时替换

因为#话题#@提及用户可以同时存在,所以需要考虑可能互相影响的问题,比如@提及用户中间可能出现#,导致前后话题的正则匹配发生错乱。

解决方案是:先使用一个@开头且与@提及用户等长的字符串替换@提及用户,再执行#话题#的正则匹配,最后再换回来。

2.整体删除

删除操作分为两步:第一次点删除仅选中整个用户名(提醒用户是整体删除);第二次点删除才真的删除文本。

func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
    if text == "" {
        for (num, user) in usersArray.enumerated() { // usersArray 用于存放已选取的用户信息
            // ②删除选中的用户名
            if textView.selectedRange.location <= user.range.location && NSMaxRange(user.range) <= NSMaxRange(textView.selectedRange) {
                textView.replace(textView.selectedTextRange ?? UITextRange(), withText: "")
                return false
            }
            // ①选中用户名
            if textView.selectedRange.length == 0 && (textView.selectedRange.location == user.range.location + user.range.length) {
                textView.selectedRange = user.range
                return false
            }
        }
    }
}

到此这篇关于iOS UITextView 实现类似微博的话题、提及功能的文章就介绍到这了,更多相关iOS UITextView微博话题内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • iOS 项目嵌入Flutter 运行(最新推荐)

    iOS 项目嵌入Flutter 运行(最新推荐)

    这篇文章主要介绍了iOS 项目嵌入Flutter 运行,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-03-03
  • iOS实现3D卡片式轮播效果

    iOS实现3D卡片式轮播效果

    这篇文章主要为大家详细介绍了iOS实现3D卡片式轮播效果,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-02-02
  • iOS正确监听手机静音键和侧边音量键的方法示例

    iOS正确监听手机静音键和侧边音量键的方法示例

    这篇文章主要给大家介绍了关于iOS正确监听手机侧边音量键的相关资料,并且给大家分享了ios监听静音键的示例代码,文中介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面来一起看看吧。
    2017-11-11
  • Flutter列表滚动定位超强辅助库使用示例详解

    Flutter列表滚动定位超强辅助库使用示例详解

    这篇文章主要为大家介绍了Flutter列表滚动定位超强辅助库使用示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-08-08
  • iOS按比例实现方块图

    iOS按比例实现方块图

    这篇文章主要为大家详细介绍了iOS按比例实现方块图,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-06-06
  • iOS设置圆角阴影 避免离屏渲染

    iOS设置圆角阴影 避免离屏渲染

    这篇文章主要为大家详细介绍了iOS设置圆角阴影,避免离屏渲染,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-04-04
  • 详解iOS App开发中session和coockie的用户数据存储处理

    详解iOS App开发中session和coockie的用户数据存储处理

    iOS在HTTP网络编程环境方面提供了NSURLSession、NSHTTPCookieStorage和NSHTTPCookie类来处理session和coockie的相关内容,接下来我们将来详解iOS App开发中session和coockie的用户数据存储处理:
    2016-06-06
  • ios可拖动按钮实例

    ios可拖动按钮实例

    下面小编就为大家分享一篇ios可拖动按钮实例。具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-03-03
  • 浅谈SwiftUI 里面$0是什么意思如何用

    浅谈SwiftUI 里面$0是什么意思如何用

    这篇文章主要介绍了浅谈SwiftUI 里面$0是什么意思如何用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-01-01
  • IOS实现输入验证码、密码按位分割(二)

    IOS实现输入验证码、密码按位分割(二)

    这篇文章主要介绍了IOS实现输入验证码、密码按位分割的方法,在App内,密码及验证码的输入,采用按位输入的方法,且位与位之间有分隔线,感兴趣的小伙伴们可以参考一下
    2016-01-01

最新评论