在 UITextView 中按单词删除 Slide-to-type 在 `shouldChangeText` 委托方法中给出了不正确的范围
Delete Slide-to-type by word in UITextView gives incorrect range in `shouldChangeText` delegate method
我在 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 委托方法中提供的范围和替换文本更新对文本视图所做的更改。为了简单起见,我没有包括它。
我只是注意到了完全相同的事情,并且努力了很长一段时间试图以一种优雅的方式解决这个问题。在我看来,这必须是一个错误,所以希望它会尽快修复。提交我想出的解决方案后,我会向 Apple 提交错误报告。
首先,我们需要一种方法来跟踪用户何时可能删除了整个单词(即,无论何时他们删除了我们必须检查的任何内容,以防他们删除的不仅仅是一个字符)。我们可以在 UITextViewDelegate
方法 textView(_:shouldChangeTextIn:replacementText:)
中做到这一点。我们知道当替换文本为空(即 text.count == 0
)时,something 被 deleted。
// 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)
...
}
}
}
我在 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 委托方法中提供的范围和替换文本更新对文本视图所做的更改。为了简单起见,我没有包括它。
我只是注意到了完全相同的事情,并且努力了很长一段时间试图以一种优雅的方式解决这个问题。在我看来,这必须是一个错误,所以希望它会尽快修复。提交我想出的解决方案后,我会向 Apple 提交错误报告。
首先,我们需要一种方法来跟踪用户何时可能删除了整个单词(即,无论何时他们删除了我们必须检查的任何内容,以防他们删除的不仅仅是一个字符)。我们可以在 UITextViewDelegate
方法 textView(_:shouldChangeTextIn:replacementText:)
中做到这一点。我们知道当替换文本为空(即 text.count == 0
)时,something 被 deleted。
// 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)
...
}
}
}