iOS中UITextView多实例场景下撤销/重做的IME适配问题
iOS 下处理 IME 输入中间状态的等效方案
针对UICollectionView复用UITextView时自定义撤销/重做遇到的IME中间状态捕获问题,iOS有直接的等效方案,核心是利用UITextInput协议的状态判断区分临时组合输入和最终确认输入:
核心判断依据:
markedTextRange
当用户通过IME输入非英文内容时,处于组合输入阶段(比如输入拼音生成汉字的过程中),UITextView的markedTextRange属性会指向当前正在编辑的临时组合文本区域(未确认的中间输入)。只有当用户确认输入(选中候选词、按下空格或回车)后,markedTextRange会变为nil,此时的文本变更才是最终状态。具体实现逻辑
放弃单纯依赖shouldChangeTextIn捕获所有文本变更,转而在textViewDidChange中结合markedTextRange过滤中间状态:// 用字典暂存不同textView的旧文本,可根据tag或唯一标识关联 private var oldTextMap: [Int: String] = [:] func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool { // 暂存当前文本,用于后续对比变更 oldTextMap[textView.tag] = textView.text return true } func textViewDidChange(_ textView: UITextView) { // 忽略IME组合输入的中间状态 guard textView.markedTextRange == nil else { return } // 取出旧文本对比,生成撤销/重做记录 guard let oldText = oldTextMap[textView.tag], let newText = textView.text else { return } // 执行自定义的撤销状态记录逻辑 recordUndoOperation(from: oldText, to: newText, for: textView) // 清理暂存数据 oldTextMap.removeValue(forKey: textView.tag) }兼容非IME场景
对于英文输入、删除、粘贴等非IME操作,markedTextRange始终为nil,上述逻辑会正常捕获文本变更,不会影响原有功能的正确性。
这个方案的核心逻辑和Android的TextWatcher.beforeTextChanged忽略中间态完全等效:通过系统提供的输入状态标识,只对用户确认后的最终文本变更进行记录,过滤掉IME组合过程中的临时输入。
内容的提问来源于stack exchange,提问作者Cheok Yan Cheng
相关产品推荐
相关产品推荐

