听写后用三指点击执行撤消时应用程序崩溃

App crashes when performing Undo with three finger tap after Dictation

我遇到了问题。鉴于我有一个 UITextField 和一个用于删除文本字段内容的按钮。

所以这按预期工作:

  1. 使用键盘输入文字
  2. 点击删除按钮
  3. 用三个手指点击并select从上下文菜单撤消
  4. 文本重新出现

现在使用听写时事情开始变得奇怪了:

  1. 点击听写按钮
  2. 说一些词,使它们出现在文本字段中
  3. 点击删除按钮
  4. 用三个手指点击并select从上下文菜单撤消
  5. 应用程序崩溃

崩溃是Terminating app due to uncaught exception 'NSRangeException', reason: 'NSMutableRLEArray replaceObjectsInRange:withObject:length:: Out of bounds'

所以听写的文本似乎并没有出现在可能的撤消列表中。有没有人知道如何解决这个问题? 到目前为止我尝试了什么:

我很乐意在这里找到解决方案!

测试应用的代码示例:

import UIKit

class ViewController: UIViewController {

    let textField = UITextField(frame: CGRect(x: 50, y: 50, width: 200, height: 44))

    override func viewDidLoad() {
        super.viewDidLoad()

        textField.layer.borderColor = UIColor.black.cgColor
        textField.layer.borderWidth = 1
        view.addSubview(textField)

        let deletebutton = UIButton(type: .custom)
        deletebutton.frame = CGRect(x: 50, y: 100, width: 100, height: 50)
        deletebutton.addTarget(self, action: #selector(deleteText(_:)), for: .touchUpInside)
        deletebutton.setTitle("delete", for: .normal)
        deletebutton.setTitleColor(.black, for: .normal)
        view.addSubview(deletebutton)
    }

    @objc func deleteText(_ sender: UIButton) {
        textField.text = nil
    }
}

更新 1

我现在通过设置 textField.clearButtonMode = .whileEditing 将清除按钮添加到文本字段,这按预期工作。 所以主要问题是:内置清除按钮与自定义按钮有什么不同?是否触发了某些通知?其他魔法?请赐教!

更新 2

执行此操作时应用程序也会崩溃:

  1. 口述某事
  2. 使用 textField.text = "Something"
  3. 将文本替换为不同的文本
  4. 三指点击并select撤消

既然没有答案,我会在找到解决方案后回答。

我无法回答为什么会发生这种情况,但这种行为会使 App Store 中广泛使用和可用的应用程序崩溃似乎很常见。

无论如何,以下代码将防止崩溃并基于列出的答案here

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
        guard let text = textField.text else {
            return true
        }

        let str = text + string
        guard str.count > 0 else {
            return false
        }

        if string.isEmpty && range.length > 0 {
            textField.text = text.count > range.length ? String(text.dropLast(range.length)) : ""
            return false
        }
        return true
    }

我很乐意帮助其他人回答这个问题!