如何在 Swift 中使用 ChangeChartersInRange?

我使用 应该改变字符范围作为动态类型搜索的一种方法。

然而我遇到了一个问题,应该改变字符范围在文本字段实际更新之前被调用:

在目标 C 中,我使用以下方法解决了这个问题:

-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
NSString * searchStr = [textField.text stringByReplacingCharactersInRange:range withString:string];


return YES;
}

然而,我试着用 Swift 来写这篇文章:

func textField(textField: UITextField!, shouldChangeCharactersInRange range: NSRange, replacementString string: String!) -> Bool {
let txtAfterUpdate:NSString = self.projectSearchTxtFld.text as NSString
txtAfterUpdate.stringByReplacingCharactersInRange(range, withString: string)


self.callMyMethod(txtAfterUpdate)
return true
}

在我得到一个值之前,这个方法还会被调用吗?

90887 次浏览

stringByReplacingCharactersInRange return a new string, so how about:

func textField(textField: UITextField!, shouldChangeCharactersInRange range: NSRange, replacementString string: String!) -> Bool {
if let text = textField.text as NSString? {
let txtAfterUpdate = text.replacingCharacters(in: range, with: string)
self.callMyMethod(txtAfterUpdate)
}
return true
}

Swift 3 & 4

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let textFieldText: NSString = (textField.text ?? "") as NSString
let txtAfterUpdate = textFieldText.replacingCharacters(in: range, with: string)
callMyMethod(txtAfterUpdate)


return true
}


func textFieldShouldClear(_ textField: UITextField) -> Bool {
callMyMethod("")
return true
}

Swift 2.2

func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
let textFieldText: NSString = textField.text ?? ""
let txtAfterUpdate = textFieldText.stringByReplacingCharactersInRange(range, withString: string)
callMyMethod(txtAfterUpdate)


return true
}


func textFieldShouldClear(textField: UITextField) -> Bool {
callMyMethod("")
return true
}

Though the textField.text property is an optional, it cannot be set to nil. Setting it to nil is changed to empty string within UITextField. In the code above, that is why textFieldText is set to empty string if textField.text is nil (via the nil coalescing operator ??).

Implementing textFieldShouldClear(_:) handles the case where the text field's clear button is visible and tapped.

In Swift 3 it would look like this:

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let text: NSString = (textField.text ?? "") as NSString
let resultString = text.replacingCharacters(in: range, with: string)


return true
}

To get the exact text in the my UITextField component in Swift 3.0 I used:

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let enteredTxt = textField.text! + string
doSomethingWithTxt(enteredTxt) //some custom method
}

Swift 3


If you want to pre-process the characters the user typed or pasted, the following solution workes like a charm

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {


let strippedString = <change replacements string so it fits your requirement - strip, trim, etc>


// replace current content with stripped content
if let replaceStart = textField.position(from: textField.beginningOfDocument, offset: range.location),
let replaceEnd = textField.position(from: replaceStart, offset: range.length),
let textRange = textField.textRange(from: replaceStart, to: replaceEnd) {


textField.replace(textRange, withText: strippedString)
}
return false
}

Find it here: https://gist.github.com/Blackjacx/2198d86442ec9b9b05c0801f4e392047

Swift 4, Swift 5

This method doesn't use NSString

// MARK: - UITextFieldDelegate


extension MyViewController: UITextFieldDelegate {
func textField(_ textField: UITextField,
shouldChangeCharactersIn range: NSRange,
replacementString string: String) -> Bool {
if let text = textField.text,
let textRange = Range(range, in: text) {
let updatedText = text.replacingCharacters(in: textRange,
with: string)
myvalidator(text: updatedText)
}
return true
}
}

Note. Be careful when you use a secured text field.

shouldChangeCharactersInRange

func textField(textField: UITextField!, shouldChangeCharactersInRange range: NSRange, replacementString string: String!) -> Bool { }

This function is called when changes are made but UI is not updated and waiting for your choice

Take a look at returned bool value

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool
  • If you return true - it means that iOS accept changes(text, caret...)
  • If you return false - it means that you are responsible for all this stuff

shouldChangeCharactersIn is called on every key press.

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
// get the current text, or use an empty string if that failed
let currentText = textField.text ?? ""


// attempt to read the range they are trying to change, or exit if we can't
guard let stringRange = Range(range, in: currentText) else { return false }


// add their new text to the existing text
let updatedText = currentText.replacingCharacters(in: stringRange, with: string)


// make sure the result is under 16 characters
return updatedText.count <= 16
}

This is essentially @Vyacheslav's answer independently arrived at for my own use case, just in case the stylistic approach resonates :-)

func textField(_ textField: UITextField, shouldChangeCharactersIn nsRange: NSRange, replacementString: String) -> Bool {
let range = Range(nsRange, in: textField.text!)!
let textWouldBecome =  textField.text!.replacingCharacters(in: range, with: replacementString)
if textWouldBecome != eventModel.title {
self.navigationItem.setHidesBackButton(true, animated: true)
} else {
self.navigationItem.setHidesBackButton(false, animated: true)
}
return true
}

Replace eventModel.title with whatever you're checking for the change against obviously.