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

时间:2019-07-19 16:36:35

标签: swift uitextview special-characters uitextviewdelegate

我有一个UITextView,可让用户输入他们刚刚录制的某些音频文件的注释。
我想将它们可以使用的“ \ n”(换行符)的数量限制为5(即,注释的长度最多为5行)。 如果他们尝试转到第六行,我想显示一条带有有意义消息的警报,并且在相对操作中按“确定”按钮时,我希望用户可以编辑其文本。

已经建立了授权,现在我正在执行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
    }
}  

不会抛出任何错误消息,因为代码完全符合我的要求,但是我显然以错误的方式提出了要求。 我该怎么办?

2 个答案:

答案 0 :(得分: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

开始

答案 1 :(得分:0)

您正在描述的逻辑之所以发生,是因为您始终在增加计数器,但从未减少计数器。如果您增加了计数器,则应该递减计数器,但是永远不要将新行添加到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
    }
}