You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift中如何判断光标是否处于成对符号之间?iOS富文本快捷输入功能优化问询

解决iOS UITextView成对符号光标追踪问题

我明白你现在的核心痛点是:只用isShortcutBtnInputing这个布尔值没法精准判断光标是否还停留在插入的成对符号之间,导致用户手动移走光标后,回车逻辑依然触发跳转,不符合预期。

要解决这个问题,我们需要记录成对符号的具体位置范围,而不是依赖一个笼统的状态标记。下面是具体的实现方案:


1. 新增变量保存活跃符号范围

首先,在你的类里添加一个可选的NSRange变量,用来记录当前插入的成对符号在文本中的位置和长度:

var activeSymbolRange: NSRange? // 存储当前成对符号的完整范围

2. 修改快捷按钮点击方法,记录符号范围

更新shortcutFunc方法,在插入符号后计算并保存这个符号块的准确范围,同时去掉原来的isShortcutBtnInputing标记(用范围判断替代):

@objc func shortcutFunc(sender: CustomBtn, forEvent: UIEvent) {
    // 先确定当前编辑的TextView
    let selectedView: CustomTextView? = bodyViewUnderEditing ? textField.bodyView : titleViewUnderEditing ? textField.titleView : nil
    guard let selectedView = selectedView else { return }
    
    // 记录插入符号前的光标位置
    let originalCursorLocation = selectedView.selectedRange.location
    // 插入成对符号
    insertFromCursor(sender: sender, forEvent: forEvent)
    
    retreat = sender.retreat!
    // 计算成对符号的完整范围:从原光标位置开始,长度为插入的符号总长度
    let totalSymbolLength = sender.argument!.count
    activeSymbolRange = NSRange(location: originalCursorLocation, length: totalSymbolLength)
    
    // 将光标移到成对符号中间
    selectedView.selectedRange = NSRange(location: originalCursorLocation + retreat, length: 0)
}

3. 更新回车逻辑,基于范围判断光标位置

修改textView(_:shouldChangeTextIn:replacementText:)方法,通过检查光标是否在activeSymbolRange范围内,来决定执行哪种回车逻辑:

func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
    if textView == textField.bodyView {
        if text == "\n", bodyViewUnderEditing {
            // 检查光标是否处于活跃的成对符号范围内
            if let symbolRange = activeSymbolRange, NSLocationInRange(range.location, symbolRange) {
                // 光标在符号中间,跳转到符号末尾
                let newCursorLocation = symbolRange.location + symbolRange.length
                textField.bodyView.selectedRange = NSRange(location: newCursorLocation, length: 0)
                activeSymbolRange = nil // 清空活跃范围,避免后续干扰
                return false
            } else {
                // 光标不在符号内,执行普通换行
                activeSymbolRange = nil
                return true
            }
        }
        
        // 额外处理:用户手动移动光标离开符号范围时,清空活跃标记
        if let symbolRange = activeSymbolRange, !NSLocationInRange(textView.selectedRange.location, symbolRange) {
            activeSymbolRange = nil
        }
        return true
    } else if textView == textField.titleView {
        if text == "\n" {
            if let symbolRange = activeSymbolRange, NSLocationInRange(range.location, symbolRange), titleViewUnderEditing {
                // 跳转到符号末尾
                let newCursorLocation = symbolRange.location + symbolRange.length
                textField.titleView.selectedRange = NSRange(location: newCursorLocation, length: 0)
                activeSymbolRange = nil
            } else {
                textField.bodyView.becomeFirstResponder()
                activeSymbolRange = nil
            }
            return false
        }
        
        // 手动移动光标离开符号范围时清空标记
        if let symbolRange = activeSymbolRange, !NSLocationInRange(textView.selectedRange.location, symbolRange) {
            activeSymbolRange = nil
        }
        return true
    }
    return true
}

方案说明

  1. 精准追踪:通过activeSymbolRange记录成对符号的具体位置,替代原来的布尔标记,能准确判断光标是否在符号区域内。
  2. 自动重置状态:当用户手动将光标移出符号范围时,自动清空activeSymbolRange,确保后续回车逻辑恢复正常。
  3. 边界处理:覆盖了用户在符号内输入、手动移走光标、按回车跳转等所有场景,逻辑更严谨。

内容的提问来源于stack exchange,提问作者Rex Zhang

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.28 14:17:41