如何在UITextView中获取光标前的字符?

3
假设我有以下的 UITextview 对象:
```UITextView```
它涉及IT技术。
var textView = UITextView()
textView.text = "Hello World!"

现在假设我不想让用户在编辑时删除“W”字符。那么我该如何知道光标前面(或被光标选中的)是哪个字符?
我需要的是类似于以下代码的实现:
if textView.characterBeforeCursor() != "W" {
   textView.deleteBackward()
}

或者...(当用户选择“W”字符时):

if textView.selectedTextContains("W") == false {
   textView.deleteBackward()
}

我该采用什么方法来完成这个任务?

修改之前和之后检查 'W的长度,如果值不同,则返回textView` 的先前值。 - Ashraf
我考虑过那种可能性,但看起来有点像“hack”。我想知道是否有更“正统”的方法。 - ThiagoAM
你尝试过使用https://developer.apple.com/documentation/uikit/uitextfielddelegate/1619599-textfield吗?你可以计算一些范围并且对输入有控制。它是你的完美选择。 - Blind Ninja
你的意思是想知道单词(由空格分隔)的第一个字母是否将要被更改?并且防止这种情况发生?还是只针对这个案例中的“W”? - ielyamani
2个回答

4

这里有一个想法,虽未经全面测试,但似乎可行...只需抓取即将被操作的字符,并在其为目标时阻止回退键...关于文本选择,如果选择包含了目标,则我们会阻止新文本的输入。

import UIKit

class ViewController: UIViewController, UITextViewDelegate {

    @IBOutlet weak var textView: UITextView!

    override func viewDidLoad() {
        super.viewDidLoad()
        self.textView.delegate = self
        // Do any additional setup after loading the view, typically from a nib.
    }

    func characterBeforeCursor() -> String? {
        // get the cursor position
        if let cursorRange = textView.selectedTextRange {
            // get the position one character before the cursor start position
            if let newPosition = textView.position(from: cursorRange.start, offset: -1) {
                let range = textView.textRange(from: newPosition, to: cursorRange.start)
                return textView.text(in: range!)
            }
        }
        return nil
    }

    func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
        if (characterBeforeCursor() == "W") {
            let char = text.cString(using: String.Encoding.utf8)!
            let isBackSpace = strcmp(char, "\\b")
            if (isBackSpace == -92) {
                return false
            }
            return true
        }
        else {
            if let range = textView.selectedTextRange {
                let selectedText = textView.text(in: range)
                if (selectedText!.contains("W")) {
                    return false
                }
            }
            return true
        }
    }
}

@ThiagoAM - 现在已经更新,即使有选择也可以工作。 - Woodstock
@ThiagoAM - 之前有个bug,但现在已经修复了,我认为它很好了,你可以看一下。 - Woodstock
1
太好了!这就是我一直在寻找的答案,谢谢! - ThiagoAM
@ThiagoAM,你有没有尝试其他更短的答案或者至少阅读过它们? - Rakesha Shastri
@ThiagoAM 很高兴认识你! - Woodstock
如果按照这种方式使用,表情符号会出现问题吗? - Binh Ho

1
这应该可以做到:

let forbiddenLetter = "W" 

func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {

    guard let txt = textView.text, let txtRange = Range(range, in: txt) else {
        return false
    }

    let subString: Substring = txt[txtRange]

    return !subString.contains(forbiddenLetter)
}

在上面的代码中,let txt = textView.text 只是为了简单起见,我们可以保持强制解包 textView.text!,因为对于非nil的UITextView,.text属性被设计为永远不会返回nil
通过 let txtRange = Range(range, in: txt),我们得到了一个类型为Range<String.Index>而不是普通的NSRange的变量range。通过这样做,我们可以获取textView即将更改的txt的子字符串。
最后,检查subString是否包含forbiddenLetter的结果被返回。
这段代码可以防止使用以下方法删除 W
  • 退格键
  • 删除选中内容
  • 粘贴覆盖选中内容
  • 自动更正(来自弹出窗口)

发现问题了,你的解决方案使用了UITextField代理方法,而不是UITextView。 - Woodstock
1
@Woodstock 感谢您指出这一点,我已经更新了答案。 - ielyamani
这是一个非常优秀的解决方案,应该标记为答案。干得漂亮,你能否注释一下你的代码以展示逻辑? - Woodstock

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接