在 UITextView 中逐字删除 Slide-to-type 在 `shouldChangeText` 委托方法中给出了不正确的范围

Posted

技术标签:

【中文标题】在 UITextView 中逐字删除 Slide-to-type 在 `shouldChangeText` 委托方法中给出了不正确的范围【英文标题】:Delete Slide-to-type by word in UITextView gives incorrect range in `shouldChangeText` delegate method 【发布时间】:2020-04-06 14:33:34 【问题描述】:

我在 ViewController 中有一个 UITextView。我已经从 UITextViewDelegate 实现了 shouldChangeTextInRange 委托方法。我通过上述删除单个字符的委托方法获得的 range 值类似于 location, 1

但是如果我在执行删除操作之前使用滑动输入机制插入一个单词,整个单词会被删除而不是单个字符。我需要对此进行跟踪。

例如,我使用滑动输入机制插入“Hello”。如果我按键盘上的删除键,我会得到 4, 1 作为范围,但整个单词“Hello”已在文本视图中被删除。在这种情况下,我需要将范围设为 0, 5,就像选择文本“Hello”并删除它一样。

有没有办法区分普通删除操作和滑动输入删除操作?如何获取文本视图中已删除的实际范围?

非常感谢您的帮助。

这是我尝试过的。

class ViewController: UIViewController 

    override func viewDidLoad() 
        super.viewDidLoad()
        self.addTextView()
    

    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) 
        self.view.endEditing(true)
    


extension ViewController 
    func addTextView() 
        let textContainer = NSTextContainer(size: CGSize(width: 200, height: CGFloat.greatestFiniteMagnitude))

        let layoutManager = NSLayoutManager()
        layoutManager.addTextContainer(textContainer)

        let textStorage = NSTextStorage()
        textStorage.delegate = self
        textStorage.addLayoutManager(layoutManager)

        let textViewFrame = CGRect(x: 50, y: 50, width: 200, height: 200)
        let textView = UITextView(frame: textViewFrame, textContainer: textContainer)
        textView.backgroundColor = .green
        textView.delegate = self

        self.view.addSubview(textView)
    


extension ViewController: UITextViewDelegate 
    func textViewDidBeginEditing(_ textView: UITextView) 
        print("text editing began")
    

    func textViewDidChange(_ textView: UITextView) 
        print("text view content changed")
    

    func textViewDidChangeSelection(_ textView: UITextView) 
        print("text view selection changed")
    

    func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool 
        print("should change \(text) in \(range)")
        return true
    

    func textViewDidEndEditing(_ textView: UITextView) 
        print("text editing ended")
    


extension ViewController: NSTextStorageDelegate 
    func textStorage(_ textStorage: NSTextStorage, willProcessEditing editedMask: NSTextStorage.EditActions, range editedRange: NSRange, changeInLength delta: Int) 
        print("will process \(editedRange)")
    

    func textStorage(_ textStorage: NSTextStorage, didProcessEditing editedMask: NSTextStorage.EditActions, range editedRange: NSRange, changeInLength delta: Int) 
        print("did process \(editedRange)")
    

编辑 1:

我有一个单独的存储机制,我根据 shouldChangeText 委托方法中提供的范围和替换文本更新对文本视图所做的更改。为了简单起见,我没有包括在内。

【问题讨论】:

【参考方案1】:

我刚刚注意到完全相同的事情,并挣扎了很长时间,试图以一种优雅的方式解决这个问题。在我看来,这必须是一个错误,所以希望它会尽快修复。提交我想出的解决方案后,我将向 Apple 提交错误报告。

首先,我们需要一种方法来跟踪用户何时可能删除了整个单词(即,每当他们删除任何内容时,我们必须检查以防他们删除了多个字符)。我们可以在UITextViewDelegate 方法textView(_:shouldChangeTextIn:replacementText:) 中做到这一点。我们知道当替换文本为空(即text.count == 0)时,something删除

// Variable to keep track of when a word might have been deleted
fileprivate var didJustDelete = false


func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool 
  if text.count > 0 
    // Code to handle adding text
    ...
   else 
    // Code to handle deleting text
    didJustDelete = true
    ...
  

然后,在textViewDidChange(另一个UITextViewDelegate方法)中,我们可以确定差异并相应地处理它。

func textViewDidChange(_ textView: UITextView) 
  // Check for deleting an entire word
  if didJustDelete 
    didJustDelete = false

    let difference = actualText.count - textView.text.count
    if difference > 0 
      // Code to handle deleting a word
      let range = NSRange(location: textView.text.count, length: difference)
      ...
    
  

【讨论】:

以上是关于在 UITextView 中逐字删除 Slide-to-type 在 `shouldChangeText` 委托方法中给出了不正确的范围的主要内容,如果未能解决你的问题,请参考以下文章

小程序地址左滑默认删除

IOS - 从 UITextView 中删除所有填充

从 UITextView 的菜单中选择和删除文本时崩溃

从Superview中删除时UITextView不会消失

在 UITextView 上删除带有 alpha 动画的 CAGradientLayer

使 UITextView 的一部分不可删除