如何防止用户在 Swift 5 的 UITextView 中添加太多换行符?

Posted

技术标签:

【中文标题】如何防止用户在 Swift 5 的 UITextView 中添加太多换行符?【英文标题】:How can I prevent a user from adding too many line breaks in a UITextView in Swift 5? 【发布时间】:2019-07-19 16:36:35 【问题描述】:

我有一个UITextView 让用户为他们刚刚录制的一些音频文件输入 cmets。 我想将他们可以使用的“\n”(换行符)的数量限制为 5(即,注释最多应为 5 行)。 如果他们尝试转到第六行,我想显示一条带有合理消息的警报,并且在相关操作中按下 OK 按钮后,我想让用户能够编辑他们的文本。

委托已经建立,optional func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool 的实施就是我现在正在做的事情。 我放入的逻辑正确地显示了警报,但是在单击“确定”并尝试删除一些字符后,再次调用该方法并收到警报。 我知道这是因为计数器仍然停留在 5,但将其重置为 0 允许 9 行或更多行,所以这不是一个解决方案。

这是我尝试过但没有按预期工作的代码:

func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool 
    if text == characterToCheck /* \n, declared globally */ 
        characterCounter += 1 // this was also declared as a global property
    

    if characterCounter > 4 
        let newlineAC = UIAlertController(title: "Too many linebreaks", message: "Please go back and make your comment fit into a maximum of 5 lines", preferredStyle: .alert)
        newlineAC.addAction(UIAlertAction(title: "OK", style: .default)  [weak self] (_) in
            let currentText = textView.text ?? ""
            guard let currentTextRange = Range(range, in: currentText) else  return 

            self?.comments.text = currentText.replacingOccurrences(of: "\n", with: "@ ", range: currentTextRange)
        )
        present(newlineAC, animated: true)

        return false
     else 
        return true
    
  

没有抛出错误消息,因为代码完全按照我的要求执行,但我显然是以错误的方式询问它。 我能做什么?

【问题讨论】:

另一件需要考虑的事情。您想如何处理不包含换行符但在文本视图中仍占用超过 5 行的文本? 请注意,您的代码假定用户一次输入一个字符。您的代码无法处理用户粘贴一堆文本。 现在我会允许这样做,并且可能会进入显示内容的表格视图并将显示的行数限制为 5 左右。对于粘贴文本的处理,我不知道如何处理,但这可能是一个想法。尽可能彻底的解决方案将不胜感激。 【参考方案1】:

这是我的解决方案:

更新了 Matt Bart 反馈

 func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool 
    if text == String(characterToCheck) /* \n, declared globally */ 
        characterCounter += 1 // this was also declared as a global property
    

    if text == "" 
        let characters = Array(textView.text)
        if characters.count >= range.location 
            let deletedCharacter = characters[range.location]
            if  deletedCharacter == characterToCheck 
                characterCounter -= 1
            
        
    

    if characterCounter > 4 
        let newlineAC = UIAlertController(title: "Too many linebreaks", message: "Please go back and make your comment fit into a maximum of 5 lines", preferredStyle: .alert)
        newlineAC.addAction(UIAlertAction(title: "OK", style: .default)  [weak self] (_) in
            let currentText = textView.text ?? ""
            guard let currentTextRange = Range(range, in: currentText) else  return 

            self?.comments.text = currentText.replacingOccurrences(of: "\n", with: "@ ", range: currentTextRange)
        )
        present(newlineAC, animated: true, completion:  [weak self] in
            self?.characterCounter -= 1
        )

        return false
     else 
        return true
    

另外,将characterToCheck 声明为Character 而不是String

characterCounter 必须以 0 开头

【讨论】:

谢谢!这解决了问题!现在,我该如何改进这一点,以便将粘贴的文本也考虑在内? 这假定他们要删除的东西是数组中的最后一个元素。情况并非总是如此:) 您还应该考虑范围变量 Matt Barr 是对的,我这里没有我的 mac,但是当我有了它时,我会改进答案。 是的,更接近,但这仍然不能解释用户何时选择并删除所有文本,而不是退格单个字符,但通过 Neera 的一些调整,它应该可以工作。【参考方案2】:

您所描述的逻辑正在发生,因为您总是递增计数器,但从不递减它。如果您增加了计数器,您应该减少计数器,但从未最终将新行添加到TextView

...
present(newlineAC, animated: true)
characterCounter-=1
return false
...

为什么要减量:

通过在此函数中返回false,TextView 不会将新的更改添加到TextView。因为没有添加,所以不应该计入characterCount

选择删除

当用户一次删除整个文本选择时,您还必须考虑到这一点。

let text = NSString(string: textView.text)
for char in text.substring(with: range) 
    print(char)
if char == characterToCheck 
        //if the text that is going to be removed has the character
        //remove it from the count      
        characterCounter-=1
    

确保如果用户删除,该函数将返回true,以便文本实际被删除。

粘贴(选择插入)

如果用户粘贴了一堆文本,我们需要检查那里的换行符。

for char in replacementString 
    if char == characterToCheck 
      characterCounter+=1
   

现在都在一起

这会考虑所有因素。也使用新变量更新了一些逻辑。

func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool 
    var localCount = characterCount
    //check for deletion
    let NStext = NSString(string: textView.text)
    for char in NStext.substring(with: range) 
        print(char)
        if char == characterToCheck 
            //if the text that is going to be removed has the character
            //remove it from the count      
            localCount-=1
        
    

    //check for insertion
    //this will also replace the simple check in the beginning
    for char in replacementString 
            if char == characterToCheck 
            //if any of the character being inserted is the one
            //will be taken into account here
                localCount+=1
             
        

    if localCount > 4 
        let newlineAC = UIAlertController(title: "Too many linebreaks", message: "Please go back and make your comment fit into a maximum of 5 lines", preferredStyle: .alert)
        newlineAC.addAction(UIAlertAction(title: "OK", style: .default)  [weak self] (_) in
            let currentText = textView.text ?? ""
            guard let currentTextRange = Range(range, in: currentText) else  return 

            self?.comments.text = currentText.replacingOccurrences(of: "\n", with: "@ ", range: currentTextRange)
        )
        present(newlineAC, animated: true)
        return false
     else 
        characterCounter = localCount
        //only updates if has an OK # of newlines
        return true
    
  

【讨论】:

您的方法是一种改进,但还不是解决方案,因为如果我然后回去擦除一行并尝试添加一个新行,即使我只有 3 个换行符,计数器在 4 时也会触发 5人物。如何解决这个问题?我想避免每次循环遍历文本以查看有多少 \n 。 我明白你在说什么,当我有机会考虑删除时会更新。删除时,replacementText 将为“”,范围将是它要替换的范围。如果该范围内有任何换行符,则应将它们从 characterCounter 递减,然后返回 false(以便删除过程正确)。 很好,但是如何告诉系统我正在删除?感谢你的付出。我将等待您提出的解决方案。我的头脑仍然需要围绕这个方法的逻辑,它实际上做了什么等等。它的文档说,如果用户点击删除它的范围是 1 这让我很困惑:为什么要声明一个与参数名称同名的text 属性?删除应该在某处有text = 0,我在第一段中看不到它。那么,replacementString 是什么?你在哪里声明的? @NotationMaster 让我纠正一下,我是分别做每个部分(然后把它们放在一起):)

以上是关于如何防止用户在 Swift 5 的 UITextView 中添加太多换行符?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Swift 中防止 UITextField 中出现空白或空格?

如何防止意外触摸触发 swift 4.2 中的 touchesBegan?

如何防止用户写除英文 Ios 以外的任何内容

如何在 iOS 上使用 swift 防止我的​​应用程序屏幕锁定

收到推送通知且应用程序在前台时如何防止弹出警报

如何防止 UILabel 在 Swift 中阻止对父 UIButton 的触摸?