在 UITextView 中以大写形式显示文本并从 shouldChangeTextIn 中获取新文本,以保持原始文本和输入文本的大小写
Posted
技术标签:
【中文标题】在 UITextView 中以大写形式显示文本并从 shouldChangeTextIn 中获取新文本,以保持原始文本和输入文本的大小写【英文标题】:Showing Text in UITextView with upppercase and getting new text from shouldChangeTextIn keeping case of original and entered text 【发布时间】:2018-05-18 20:12:54 【问题描述】:我有一个 UITextView,有时我需要以全部大写形式显示,有时以原始情况显示。这部分很简单,但是当用户开始编辑文本时,我需要使用更改更新原始字符串,同时保留字符串的原始大小写。出于这个原因,我无法使用 textDidChange 而是使用 shouldChangeTextIn 并更改原始字符串。大多数情况下,一切都按预期工作,除非选择了多个单词并且您在键盘上点击预测词。可能还有其他事情会破坏。解决此问题以在显示变异字符串的同时保留原始字符串的最佳方法是什么。这是一个最小的例子。
import UIKit
class ViewController: UIViewController
lazy var textView : UITextView =
let txtv = UITextView(frame: CGRect(x: 0, y:40, width: self.view.frame.width, height: 200))
txtv.autoresizingMask = [.flexibleWidth,.flexibleHeight]
txtv.delegate = self
txtv.font = UIFont.systemFont(ofSize: 22)
return txtv
()
lazy var button : UIButton =
let btn = UIButton(frame: CGRect(x: 0, y:250, width: self.view.frame.width - 40, height: 50))
btn.autoresizingMask = [.flexibleWidth,.flexibleHeight]
btn.setTitleColor(UIColor.blue, for: .normal)
btn.setTitle("SHOW TEXT", for: .normal)
btn.addTarget(self, action: #selector(changeState), for: .touchUpInside)
return btn
()
var textString = "This is a test to see how we are doing"
var shouldCapitalize : Bool = true
private var hack_shouldIgnorePredictiveInput = false
override func viewDidLoad()
super.viewDidLoad()
self.view.addSubview(textView)
self.view.addSubview(button)
//get things going
updateTextView()
func updateTextView()
//we could change font size here or size of textview
var textToShow = self.textString
if shouldCapitalize == true
textToShow = textToShow.uppercased()
textView.text = textToShow
print("the text string we really care about is::::: \(textString)")
@objc func changeState()
shouldCapitalize = !shouldCapitalize
updateTextView()
extension ViewController : UITextViewDelegate
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool
if hack_shouldIgnorePredictiveInput
hack_shouldIgnorePredictiveInput = false
return false
hack_shouldIgnorePredictiveInput = true
//how do i replace the characters or text changging without changing the case of the original string
print("the textview text is \(textView.text)")
print("the textview text is \((textView.text as NSString).length)")
print("the range is \(range)")
print("the text is \(text)")
let selectedRange = self.textView.selectedRange
if let str = textView.text as? NSString
if range.length == 0 && text == ""
print("the ext is empty")
textString = ""
updateTextView()
if let tracker = textString as? NSString
if range.location == tracker.length
print("adding")
print("the lenght of the tracker is \(tracker.length)")
let newRange = NSMakeRange(tracker.length, range.length)
print("the new range is \(newRange)")
let newString = tracker.replacingCharacters(in: newRange, with: text)
print("the new text is \(newString)")
textString = newString
updateTextView()
else if range.location < tracker.length
let newString = tracker.replacingCharacters(in: range, with: text)
print("the new text is \(newString)")
textString = newString
updateTextView()
if (newString as NSString).length > tracker.length
print("setting cursor \(NSMakeRange(range.location + range.length, 0))")
self.textView.selectedRange = NSMakeRange(range.location + range.length + 1, 0)
else
self.textView.selectedRange = NSMakeRange(range.location, 0)
else
//the problem seems to be in here
print("maybe adding")
print("the lenght of the tracker is \(tracker.length)")
let newRange = NSMakeRange(tracker.length, range.length)
print("the new range is \(newRange)")
let newString = str.replacingCharacters(in: newRange, with: text)
print("the new text is \(newString)")
textString = newString
updateTextView()
hack_shouldIgnorePredictiveInput = false
return false
【问题讨论】:
您最后的 else 语句的注释为//the problem seems to be in here
。只有range.location
大于tracker.length
因为其他if...else
已经捕获了其他情况,您才会进入其他情况。似乎让range.location
大于tracker.length
的唯一方法是将textString = ""
设置在您检查range.length == 0 && text == ""
的上方。我不太明白这一切。也许您可以提供打印语句的输出以获得更多上下文。为每个打印语句提供唯一的文本以避免歧义。
@jimmyg 你帮我指出我的最后一个 else 不应该是必要的,当我写它时我认为它是不需要的。它被调用的原因是 textview 试图添加一个带有预测文本的空格,我拦截了它并将字符串设置为空,这使得最后一个 else 被命中。我想我现在明白了,但感谢你触发了我的直觉。
【参考方案1】:
因此,似乎我的底部 else 被调用了,因为文本被设置为空,这是由于有时会调用委托两次以输入空格这一事实驱动的。来自 jimmyg 的评论帮助我看到他是对的,最后一个 else 永远不应该被击中,这是我在编写函数时的想法。幸运的是,answer 帮助解释了为什么会出现空文本。如果您在这里,我建议您投票赞成该答案。我还建议对这个answer 进行投票,这有助于阻止一些带有预测文本的不必要的调用,并且可以在我的代码中看到。最后,所有逻辑似乎都已到位,并且更加简单,并且在 UITextView 内的预期文本和转换后的文本之间切换正在工作。
import UIKit
class ViewController: UIViewController
lazy var textView : UITextView =
let txtv = UITextView(frame: CGRect(x: 0, y:40, width: self.view.frame.width, height: 200))
txtv.autoresizingMask = [.flexibleWidth,.flexibleHeight]
txtv.delegate = self
txtv.font = UIFont.systemFont(ofSize: 22)
return txtv
()
lazy var button : UIButton =
let btn = UIButton(frame: CGRect(x: 0, y:250, width: self.view.frame.width - 40, height: 50))
btn.autoresizingMask = [.flexibleWidth,.flexibleHeight]
btn.setTitleColor(UIColor.blue, for: .normal)
btn.setTitle("SHOW TEXT", for: .normal)
btn.addTarget(self, action: #selector(changeState), for: .touchUpInside)
return btn
()
var textString = "This is a test to see how we are doing"
var shouldCapitalize : Bool = true
private var hack_shouldIgnorePredictiveInput = false
override func viewDidLoad()
super.viewDidLoad()
self.view.addSubview(textView)
self.view.addSubview(button)
//get things going
updateTextView()
func updateTextView()
//we could change font size here or size of textview
var textToShow = self.textString
if shouldCapitalize == true
textToShow = textToShow.uppercased()
textView.text = textToShow
print("realString:: \(textString)")
print("textView showing:: \(textView.text)")
@objc func changeState()
shouldCapitalize = !shouldCapitalize
updateTextView()
extension ViewController : UITextViewDelegate
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool
if hack_shouldIgnorePredictiveInput
hack_shouldIgnorePredictiveInput = false
return false
hack_shouldIgnorePredictiveInput = true
if range.length == 0 && text == "" && self.textString.count > 0 && range.length == self.textString.count
textString = ""
updateTextView()
let tracker = (textString as NSString)
if range.location == tracker.length
let newRange = NSMakeRange(tracker.length, range.length)
let newString = tracker.replacingCharacters(in: newRange, with: text)
textString = newString
updateTextView()
else if range.location < tracker.length
if text.isEmpty && range.length == 0 && range.location > 0
let newString = tracker.replacingCharacters(in:range, with: text)
textString = newString
updateTextView()
self.textView.selectedRange = NSMakeRange(range.upperBound, 0)
else
let newString = tracker.replacingCharacters(in: range, with: text)
textString = newString
updateTextView()
self.textView.selectedRange = NSMakeRange(range.upperBound - tracker.length + (newString as NSString).length , 0)
hack_shouldIgnorePredictiveInput = false
return false
【讨论】:
以上是关于在 UITextView 中以大写形式显示文本并从 shouldChangeTextIn 中获取新文本,以保持原始文本和输入文本的大小写的主要内容,如果未能解决你的问题,请参考以下文章