在 UITextField 中检测退格事件
Detect backspace Event in UITextField
我正在寻找有关如何捕获退格事件的解决方案,大多数 Stack Overflow 答案都在 Objective-C 中,但我需要 Swift 语言。
首先,我为 UITextField 设置了委托并将其设置为 self
self.textField.delegate = self;
然后我知道使用 shouldChangeCharactersInRange
委托方法来检测是否按下了退格键,所有代码都在 Objective-C 中。我需要在 Swift 中使用以下这些方法。
-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
const char * _char = [string cStringUsingEncoding:NSUTF8StringEncoding];
int isBackSpace = strcmp(_char, "\b");
if (isBackSpace == -8) {
// NSLog(@"Backspace was pressed");
}
return YES;
}
Swift 4.2
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
if let char = string.cString(using: String.Encoding.utf8) {
let isBackSpace = strcmp(char, "\b")
if (isBackSpace == -92) {
print("Backspace was pressed")
}
}
return true
}
旧 Swift 版本
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
let char = string.cStringUsingEncoding(NSUTF8StringEncoding)!
let isBackSpace = strcmp(char, "\b")
if (isBackSpace == -92) {
println("Backspace was pressed")
}
return true
}
试试这个
public func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
if(string == "") {
print("Backspace pressed");
return true;
}
}
注意:如果要允许退格,可以 return "true"。否则你可以 return "false".
如果您甚至需要在空文本字段中检测退格键(例如,如果您需要在按下退格键时自动切换回上一个文本字段),您可以使用建议方法的组合 - 添加不可见符号并使用标准委托方法 textField:shouldChangeCharactersInRange:replacementString:
点赞关注
创建隐形标志
private struct Constants {
static let InvisibleSign = "\u{200B}"
}
为文本字段设置委托
textField.delegate = self
On event EditingChanged
检查文本,如果需要添加不可见符号,如下所示:
@IBAction func didChangeEditingInTextField(sender: UITextField) {
if var text = sender.text {
if text.characters.count == 1 && text != Constants.InvisibleSign {
text = Constants.InvisibleSign.stringByAppendingString(text)
sender.text = text
}
}
}
添加委托方法的实现textField:shouldChangeCharactersInRange:replacementString:
extension UIViewController : UITextFieldDelegate {
// MARK: - UITextFieldDelegate
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
let char = string.cStringUsingEncoding(NSUTF8StringEncoding)!
let isBackSpace = strcmp(char, "\b")
if (isBackSpace == -92) {
if var string = textField.text {
string = string.stringByReplacingOccurrencesOfString(Constants.InvisibleSign, withString: "")
if string.characters.count == 1 {
//last visible character, if needed u can skip replacement and detect once even in empty text field
//for example u can switch to prev textField
//do stuff here
}
}
}
return true
}
}
在Swift3
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
let char = string.cString(using: String.Encoding.utf8)!
let isBackSpace = strcmp(char, "\b")
if (isBackSpace == -92) {
print("Backspace was pressed")
}
return true
}
:)
Swift 4: 如果用户按下退格按钮,字符串为空,所以这种方法强制 textField 只接受来自指定字符集的字符(在此case utf8 字符)和退格键(string.isEmpty case)。
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if string.cString(using: String.Encoding.utf8) != nil {
return true
} else if string.isEmpty {
return true
} else {
return false
}
}
我更喜欢子类化 UITextField
和覆盖 deleteBackward()
因为这比使用 shouldChangeCharactersInRange
:
的 hack 更可靠
class MyTextField: UITextField {
override public func deleteBackward() {
if text == "" {
// do something when backspace is tapped/entered in an empty text field
}
// do something for every backspace
super.deleteBackward()
}
}
shouldChangeCharactersInRange
hack 与放置在文本字段中的不可见字符相结合有几个缺点:
- 连接键盘后,可以将光标放在不可见字符之前,并且不再检测到退格键,
- 用户甚至可以 select 那个不可见的字符(使用
Shift Arrow
在键盘上,甚至通过点击插入符号)并且会对那个奇怪的字符感到困惑,
- 只要只有这个不可见的字符,自动完成栏就会提供奇怪的选择,
- 具有基于文本字段文本的候选选项的亚洲语言键盘将会混淆,
placeholder
不再显示,
- 即使
clearButtonMode = .whileEditing
不应该显示清除按钮。
当然,由于子类化的需要,覆盖deleteBackward()
有点不方便。但更好的 UX 值得付出努力!
如果子类化是 no-go,例如当使用 UISearchBar
及其嵌入的 UITextField
时,方法调配也应该没问题。
Swift 4
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let char = string.cString(using: String.Encoding.utf8)!
let isBackSpace = strcmp(char, "\b")
if isBackSpace == -92 {
print("Backspace was pressed")
return false
}
}
Swift 5.3
在某些版本中它发生了变化,现在它说:
When the user deletes one or more characters, the replacement string
is empty.
所以回答这个:
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if string.isEmpty {
// do something
}
return true
}
如果要检测某些字符会被删除
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if range.length > 0 {
// We convert string to NSString instead of NSRange to Range<Int>
// because NSRange and NSString not counts emoji as one character
let replacedCharacters = (string as NSString).substring(with: range)
}
return true
}
如果您想在空文本字段上检测退格键
class TextField: UITextField {
var backspaceCalled: (()->())?
override func deleteBackward() {
super.deleteBackward()
backspaceCalled?()
}
}
旧答案
请不要丢弃您的代码。只需将此扩展放在代码中的某个位置即可。
extension String {
var isBackspace: Bool {
let char = self.cString(using: String.Encoding.utf8)!
return strcmp(char, "\b") == -92
}
}
然后在你的函数中使用它
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if string.isBackspace {
// do something
}
return true
}
Swift 4
我发现使用 strcmp
的比较无关紧要。我们甚至不知道 strcmp
在 hoods.In 后面是如何运作的,所有其他答案在比较当前 char 和 \b
结果时 -8
在 objective-C 和 -92
在 Swift 中。我写这个答案是因为上述解决方案对我不起作用。 ( Xcode 版本 9.3 (9E145)
使用 Swift 4.1
)
仅供参考: 您实际键入的每个字符都是 1
或 utf8 Encoding
中更多元素的数组。 backSpace 字符是 [0]
。你可以试试这个。
PS : 不要忘记将正确的 delegates
分配给您的 textFields
.
public func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let char = string.cString(using: String.Encoding.utf8)!
if (char.elementsEqual([0])) {
print("Backspace was pressed")
}
else {
print("WHAT DOES THE FOX SAY ?\n")
print(char)
}
return true
}
Swift 4
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
//MARK:- If Delete button click
let char = string.cString(using: String.Encoding.utf8)!
let isBackSpace = strcmp(char, "\b")
if (isBackSpace == -92) {
print("Backspace was pressed")
return true
}
}
我实现了这个功能:
并且在最后一个textFiled为空的情况下,我只想切换到之前的textFiled。我尝试了上面的所有答案,但没有一个适合我的情况。出于某种原因,如果我在 isBackSpace == -92
括号中添加比 print
更多的逻辑,则此方法将停止工作...
至于我,下面的方法更优雅,而且很有魅力:
Swift
class YourTextField: UITextField {
// MARK: Life cycle
override func awakeFromNib() {
super.awakeFromNib()
}
// MARK: Methods
override func deleteBackward() {
super.deleteBackward()
print("deleteBackward")
}
}
感谢@LombaX answer
Swift 5: 根据文本视图委托 documentation,如果 shouldChangeTextIn 方法返回的替换文本为空,则用户按下了退格键按钮。如果范围上限大于范围下限(或范围计数为 1 或更多),则应删除文本。如果范围上限和下限相同(或都等于 0),则没有要删除的文本(长度为 0 的范围将被替换为任何内容)。我测试过,即使在空文本字段上也会调用它。
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
guard text.isEmpty else { return true } // No backspace pressed
if (range.upperBound > range.lowerBound) {
print("Backspace pressed")
} else if (range.upperBound == range.lowerBound) {
print("Backspace pressed but no text to delete")
if (textView.text.isEmpty) || (textView.text == nil) {
print("Text view is empty")
}
}
return true
}
我正在寻找有关如何捕获退格事件的解决方案,大多数 Stack Overflow 答案都在 Objective-C 中,但我需要 Swift 语言。
首先,我为 UITextField 设置了委托并将其设置为 self
self.textField.delegate = self;
然后我知道使用 shouldChangeCharactersInRange
委托方法来检测是否按下了退格键,所有代码都在 Objective-C 中。我需要在 Swift 中使用以下这些方法。
-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
const char * _char = [string cStringUsingEncoding:NSUTF8StringEncoding];
int isBackSpace = strcmp(_char, "\b");
if (isBackSpace == -8) {
// NSLog(@"Backspace was pressed");
}
return YES;
}
Swift 4.2
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
if let char = string.cString(using: String.Encoding.utf8) {
let isBackSpace = strcmp(char, "\b")
if (isBackSpace == -92) {
print("Backspace was pressed")
}
}
return true
}
旧 Swift 版本
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
let char = string.cStringUsingEncoding(NSUTF8StringEncoding)!
let isBackSpace = strcmp(char, "\b")
if (isBackSpace == -92) {
println("Backspace was pressed")
}
return true
}
试试这个
public func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
if(string == "") {
print("Backspace pressed");
return true;
}
}
注意:如果要允许退格,可以 return "true"。否则你可以 return "false".
如果您甚至需要在空文本字段中检测退格键(例如,如果您需要在按下退格键时自动切换回上一个文本字段),您可以使用建议方法的组合 - 添加不可见符号并使用标准委托方法 textField:shouldChangeCharactersInRange:replacementString:
点赞关注
创建隐形标志
private struct Constants { static let InvisibleSign = "\u{200B}" }
为文本字段设置委托
textField.delegate = self
On event
EditingChanged
检查文本,如果需要添加不可见符号,如下所示:@IBAction func didChangeEditingInTextField(sender: UITextField) { if var text = sender.text { if text.characters.count == 1 && text != Constants.InvisibleSign { text = Constants.InvisibleSign.stringByAppendingString(text) sender.text = text } } }
添加委托方法的实现
textField:shouldChangeCharactersInRange:replacementString:
extension UIViewController : UITextFieldDelegate { // MARK: - UITextFieldDelegate func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool { let char = string.cStringUsingEncoding(NSUTF8StringEncoding)! let isBackSpace = strcmp(char, "\b") if (isBackSpace == -92) { if var string = textField.text { string = string.stringByReplacingOccurrencesOfString(Constants.InvisibleSign, withString: "") if string.characters.count == 1 { //last visible character, if needed u can skip replacement and detect once even in empty text field //for example u can switch to prev textField //do stuff here } } } return true } }
在Swift3
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
let char = string.cString(using: String.Encoding.utf8)!
let isBackSpace = strcmp(char, "\b")
if (isBackSpace == -92) {
print("Backspace was pressed")
}
return true
}
:)
Swift 4: 如果用户按下退格按钮,字符串为空,所以这种方法强制 textField 只接受来自指定字符集的字符(在此case utf8 字符)和退格键(string.isEmpty case)。
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if string.cString(using: String.Encoding.utf8) != nil {
return true
} else if string.isEmpty {
return true
} else {
return false
}
}
我更喜欢子类化 UITextField
和覆盖 deleteBackward()
因为这比使用 shouldChangeCharactersInRange
:
class MyTextField: UITextField {
override public func deleteBackward() {
if text == "" {
// do something when backspace is tapped/entered in an empty text field
}
// do something for every backspace
super.deleteBackward()
}
}
shouldChangeCharactersInRange
hack 与放置在文本字段中的不可见字符相结合有几个缺点:
- 连接键盘后,可以将光标放在不可见字符之前,并且不再检测到退格键,
- 用户甚至可以 select 那个不可见的字符(使用
Shift Arrow
在键盘上,甚至通过点击插入符号)并且会对那个奇怪的字符感到困惑, - 只要只有这个不可见的字符,自动完成栏就会提供奇怪的选择,
- 具有基于文本字段文本的候选选项的亚洲语言键盘将会混淆,
placeholder
不再显示,- 即使
clearButtonMode = .whileEditing
不应该显示清除按钮。
当然,由于子类化的需要,覆盖deleteBackward()
有点不方便。但更好的 UX 值得付出努力!
如果子类化是 no-go,例如当使用 UISearchBar
及其嵌入的 UITextField
时,方法调配也应该没问题。
Swift 4
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let char = string.cString(using: String.Encoding.utf8)!
let isBackSpace = strcmp(char, "\b")
if isBackSpace == -92 {
print("Backspace was pressed")
return false
}
}
Swift 5.3
在某些版本中它发生了变化,现在它说:
When the user deletes one or more characters, the replacement string is empty.
所以回答这个:
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if string.isEmpty {
// do something
}
return true
}
如果要检测某些字符会被删除
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if range.length > 0 {
// We convert string to NSString instead of NSRange to Range<Int>
// because NSRange and NSString not counts emoji as one character
let replacedCharacters = (string as NSString).substring(with: range)
}
return true
}
如果您想在空文本字段上检测退格键
class TextField: UITextField {
var backspaceCalled: (()->())?
override func deleteBackward() {
super.deleteBackward()
backspaceCalled?()
}
}
旧答案
请不要丢弃您的代码。只需将此扩展放在代码中的某个位置即可。
extension String {
var isBackspace: Bool {
let char = self.cString(using: String.Encoding.utf8)!
return strcmp(char, "\b") == -92
}
}
然后在你的函数中使用它
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if string.isBackspace {
// do something
}
return true
}
Swift 4
我发现使用 strcmp
的比较无关紧要。我们甚至不知道 strcmp
在 hoods.In 后面是如何运作的,所有其他答案在比较当前 char 和 \b
结果时 -8
在 objective-C 和 -92
在 Swift 中。我写这个答案是因为上述解决方案对我不起作用。 ( Xcode 版本 9.3 (9E145)
使用 Swift 4.1
)
仅供参考: 您实际键入的每个字符都是 1
或 utf8 Encoding
中更多元素的数组。 backSpace 字符是 [0]
。你可以试试这个。
PS : 不要忘记将正确的 delegates
分配给您的 textFields
.
public func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let char = string.cString(using: String.Encoding.utf8)!
if (char.elementsEqual([0])) {
print("Backspace was pressed")
}
else {
print("WHAT DOES THE FOX SAY ?\n")
print(char)
}
return true
}
Swift 4
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
//MARK:- If Delete button click
let char = string.cString(using: String.Encoding.utf8)!
let isBackSpace = strcmp(char, "\b")
if (isBackSpace == -92) {
print("Backspace was pressed")
return true
}
}
我实现了这个功能:
并且在最后一个textFiled为空的情况下,我只想切换到之前的textFiled。我尝试了上面的所有答案,但没有一个适合我的情况。出于某种原因,如果我在 isBackSpace == -92
括号中添加比 print
更多的逻辑,则此方法将停止工作...
至于我,下面的方法更优雅,而且很有魅力:
Swift
class YourTextField: UITextField {
// MARK: Life cycle
override func awakeFromNib() {
super.awakeFromNib()
}
// MARK: Methods
override func deleteBackward() {
super.deleteBackward()
print("deleteBackward")
}
}
感谢@LombaX answer
Swift 5: 根据文本视图委托 documentation,如果 shouldChangeTextIn 方法返回的替换文本为空,则用户按下了退格键按钮。如果范围上限大于范围下限(或范围计数为 1 或更多),则应删除文本。如果范围上限和下限相同(或都等于 0),则没有要删除的文本(长度为 0 的范围将被替换为任何内容)。我测试过,即使在空文本字段上也会调用它。
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
guard text.isEmpty else { return true } // No backspace pressed
if (range.upperBound > range.lowerBound) {
print("Backspace pressed")
} else if (range.upperBound == range.lowerBound) {
print("Backspace pressed but no text to delete")
if (textView.text.isEmpty) || (textView.text == nil) {
print("Text view is empty")
}
}
return true
}