如何在 iOS 中触发 textField(_:shouldChangeCharactersIn:replacementString:) 方法
How to trigger textField(_:shouldChangeCharactersIn:replacementString:) method in iOS
我正在尝试在 UITextField
上制作一些功能,用于输入数量
包装器视图包含此 UITextField
,并且该视图符合 UITextFieldDelegate
所以我在 Wrapper 视图中实现 textField(_:shouldChangeCharactersIn:replacementString:)
方法 class
在那个方法中,我从键盘操作输入文本(当数量超过最大输入数量时添加逗号或块输入)
它在系统键盘上运行良好,但在自定义键盘上运行不佳
在自定义键盘中,我制作按钮,如果按下某个按钮,我将文本设置为我制作的文本字段(数量文本字段),如下所示
textField.text = someText
但是将文本显式设置为文本字段,它不会触发 textField(_:shouldChangeCharactersIn:replacementString:)
方法
如何在使用自定义键盘时触发 textField(_:shouldChangeCharactersIn:replacementString:)
方法
你不能“触发”shouldChangeCharactersIn
,但有多种方法可以解决这个问题。
一种方法是将您的“验证”代码移动到它自己的函数中。然后,您可以从 shouldChangeCharactersIn
和自定义键盘输入中调用该函数。
所以,对于一个非常简单的例子——只允许输入数字——它可能看起来像这样:
func myShouldChangeCharacters(_ textField: UITextField, in range: NSRange, replacementString string: String) -> Bool {
// whatever you want to do to validate the input
if !"0123456789".contains(string) {
return false
}
return true
}
// textField delegate call
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
return myShouldChangeCharacters(textField, in: range, replacementString: string)
}
// button tapped
@objc func btnTapped(_ sender: Any) {
// make sure we're
// called by a button
// get the button title
// convert textField's .selectedTextRange to a NSRange
guard let btn = sender as? UIButton,
let str = btn.currentTitle,
let rng = myTextField.selectedRange
else {
return
}
if myShouldChangeCharacters(myTextField, in: rng, replacementString: str) {
myTextField.insertText(str)
}
}
使用此扩展名:
// helper extension to convert
// text field's selectedTextRange
// to a NSRange
extension UITextInput {
var selectedRange: NSRange? {
guard let range = selectedTextRange else { return nil }
let location = offset(from: beginningOfDocument, to: range.start)
let length = offset(from: range.start, to: range.end)
return NSRange(location: location, length: length)
}
}
这是一个完整的示例,顶部有一个文本字段,下面有 4 个按钮,分别标记为 1、2、A、B,并且只允许使用数字:
class ViewController: UIViewController, UITextFieldDelegate {
let myTextField: UITextField = {
let v = UITextField()
v.borderStyle = .roundedRect
return v
}()
override func viewDidLoad() {
super.viewDidLoad()
let btnsStack: UIStackView = {
let v = UIStackView()
v.axis = .horizontal
v.distribution = .fillEqually
v.spacing = 40.0
return v
}()
// create 4 buttons
["1", "2", "A", "B"].forEach { str in
let b = UIButton()
b.setTitle(str, for: [])
b.setTitleColor(.white, for: .normal)
b.setTitleColor(.gray, for: .highlighted)
b.backgroundColor = .systemRed
b.addTarget(self, action: #selector(btnTapped(_:)), for: .touchUpInside)
btnsStack.addArrangedSubview(b)
}
[myTextField, btnsStack].forEach { v in
v.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(v)
}
// respect safe area
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
myTextField.topAnchor.constraint(equalTo: g.topAnchor, constant: 40.0),
myTextField.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 40.0),
myTextField.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -40.0),
btnsStack.topAnchor.constraint(equalTo: myTextField.bottomAnchor, constant: 20.0),
btnsStack.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 40.0),
btnsStack.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -40.0),
])
myTextField.delegate = self
}
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
return myShouldChangeCharacters(textField, in: range, replacementString: string)
}
@objc func btnTapped(_ sender: Any) {
// make sure we're
// called by a button
// get the button title
// convert textField's .selectedTextRange to a NSRange
guard let btn = sender as? UIButton,
let str = btn.currentTitle,
let rng = myTextField.selectedRange
else {
return
}
if myShouldChangeCharacters(myTextField, in: rng, replacementString: str) {
myTextField.insertText(str)
}
}
func myShouldChangeCharacters(_ textField: UITextField, in range: NSRange, replacementString string: String) -> Bool {
if !"0123456789".contains(string) {
return false
}
return true
}
}
// helper extension to convert
// text field's selectedTextRange
// to a NSRange
extension UITextInput {
var selectedRange: NSRange? {
guard let range = selectedTextRange else { return nil }
let location = offset(from: beginningOfDocument, to: range.start)
let length = offset(from: range.start, to: range.end)
return NSRange(location: location, length: length)
}
}
我正在尝试在 UITextField
上制作一些功能,用于输入数量
包装器视图包含此 UITextField
,并且该视图符合 UITextFieldDelegate
所以我在 Wrapper 视图中实现 textField(_:shouldChangeCharactersIn:replacementString:)
方法 class
在那个方法中,我从键盘操作输入文本(当数量超过最大输入数量时添加逗号或块输入)
它在系统键盘上运行良好,但在自定义键盘上运行不佳
在自定义键盘中,我制作按钮,如果按下某个按钮,我将文本设置为我制作的文本字段(数量文本字段),如下所示
textField.text = someText
但是将文本显式设置为文本字段,它不会触发 textField(_:shouldChangeCharactersIn:replacementString:)
方法
如何在使用自定义键盘时触发 textField(_:shouldChangeCharactersIn:replacementString:)
方法
你不能“触发”shouldChangeCharactersIn
,但有多种方法可以解决这个问题。
一种方法是将您的“验证”代码移动到它自己的函数中。然后,您可以从 shouldChangeCharactersIn
和自定义键盘输入中调用该函数。
所以,对于一个非常简单的例子——只允许输入数字——它可能看起来像这样:
func myShouldChangeCharacters(_ textField: UITextField, in range: NSRange, replacementString string: String) -> Bool {
// whatever you want to do to validate the input
if !"0123456789".contains(string) {
return false
}
return true
}
// textField delegate call
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
return myShouldChangeCharacters(textField, in: range, replacementString: string)
}
// button tapped
@objc func btnTapped(_ sender: Any) {
// make sure we're
// called by a button
// get the button title
// convert textField's .selectedTextRange to a NSRange
guard let btn = sender as? UIButton,
let str = btn.currentTitle,
let rng = myTextField.selectedRange
else {
return
}
if myShouldChangeCharacters(myTextField, in: rng, replacementString: str) {
myTextField.insertText(str)
}
}
使用此扩展名:
// helper extension to convert
// text field's selectedTextRange
// to a NSRange
extension UITextInput {
var selectedRange: NSRange? {
guard let range = selectedTextRange else { return nil }
let location = offset(from: beginningOfDocument, to: range.start)
let length = offset(from: range.start, to: range.end)
return NSRange(location: location, length: length)
}
}
这是一个完整的示例,顶部有一个文本字段,下面有 4 个按钮,分别标记为 1、2、A、B,并且只允许使用数字:
class ViewController: UIViewController, UITextFieldDelegate {
let myTextField: UITextField = {
let v = UITextField()
v.borderStyle = .roundedRect
return v
}()
override func viewDidLoad() {
super.viewDidLoad()
let btnsStack: UIStackView = {
let v = UIStackView()
v.axis = .horizontal
v.distribution = .fillEqually
v.spacing = 40.0
return v
}()
// create 4 buttons
["1", "2", "A", "B"].forEach { str in
let b = UIButton()
b.setTitle(str, for: [])
b.setTitleColor(.white, for: .normal)
b.setTitleColor(.gray, for: .highlighted)
b.backgroundColor = .systemRed
b.addTarget(self, action: #selector(btnTapped(_:)), for: .touchUpInside)
btnsStack.addArrangedSubview(b)
}
[myTextField, btnsStack].forEach { v in
v.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(v)
}
// respect safe area
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
myTextField.topAnchor.constraint(equalTo: g.topAnchor, constant: 40.0),
myTextField.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 40.0),
myTextField.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -40.0),
btnsStack.topAnchor.constraint(equalTo: myTextField.bottomAnchor, constant: 20.0),
btnsStack.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 40.0),
btnsStack.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -40.0),
])
myTextField.delegate = self
}
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
return myShouldChangeCharacters(textField, in: range, replacementString: string)
}
@objc func btnTapped(_ sender: Any) {
// make sure we're
// called by a button
// get the button title
// convert textField's .selectedTextRange to a NSRange
guard let btn = sender as? UIButton,
let str = btn.currentTitle,
let rng = myTextField.selectedRange
else {
return
}
if myShouldChangeCharacters(myTextField, in: rng, replacementString: str) {
myTextField.insertText(str)
}
}
func myShouldChangeCharacters(_ textField: UITextField, in range: NSRange, replacementString string: String) -> Bool {
if !"0123456789".contains(string) {
return false
}
return true
}
}
// helper extension to convert
// text field's selectedTextRange
// to a NSRange
extension UITextInput {
var selectedRange: NSRange? {
guard let range = selectedTextRange else { return nil }
let location = offset(from: beginningOfDocument, to: range.start)
let length = offset(from: range.start, to: range.end)
return NSRange(location: location, length: length)
}
}