在 Swift 中设置 UITextField 的最大字符长度

我知道这方面还有其他话题,但我似乎找不到如何实现它。

我试图将 UITextField 限制为只有5个字符。

最好是字母数字,-._

我见过这个代码:

func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange,
replacementString string: String) -> Bool
{
let maxLength = 4
let currentString: NSString = textField.text
let newString: NSString =
currentString.stringByReplacingCharactersInRange(range, withString: string)
return newString.length <= maxLength
}

还有

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


let length = count(textField.text.utf16) + count(string.utf16) - range.length
return length <= 10
}

我如何实际地实现它? 我应该为我的自定义名为 UITextField 交换哪个“ textfield”?

168082 次浏览
  1. 您的视图控制器应该符合 UITextFieldDelegate,如下所示:

    class MyViewController: UIViewController, UITextFieldDelegate {
    
    
    }
    
  2. 设置 textfield 的委托: myTextField.delegate = self

  3. 在视图控制器中实现该方法:

    textField(_:shouldChangeCharactersInRange:replacementString:)
    

所有人一起:

class MyViewController: UIViewController, UITextFieldDelegate  // Set delegate to class


@IBOutlet var mytextField: UITextField             //  textfield variable


override func viewDidLoad() {
super.viewDidLoad()
mytextField.delegate = self                  // set delegate
}




func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange,
replacementString string: String) -> Bool
{
let maxLength = 4
let currentString: NSString = textField.text
let newString: NSString =  currentString.stringByReplacingCharactersInRange(range, withString: string)


return newString.length <= maxLength
}

为了 Swift 4

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


return newString.length <= maxLength
}

For Swift 5

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


return newString.count <= maxLength
}

只允许在给定的文本字段中输入指定的字符集

func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
var result = true


if mytextField == textField {
if count(string) > 0 {
let disallowedCharacterSet = NSCharacterSet(charactersInString: "0123456789.-").invertedSet
let replacementStringIsLegal = string.rangeOfCharacterFromSet(disallowedCharacterSet) == nil
result = replacementStringIsLegal
}
}


return result
}

如何编写一个 iOS 文本字段,只接受最大长度的数字输入

我有一些东西要补充到 阿拉丁的回答:

  1. Your view controller should conform to UITextFieldDelegate

     class MyViewController: UIViewController, UITextViewDelegate {
    
    
    }
    
  2. 设置 textfield 的委托:

    要设置委托,您可以控制从文本字段拖动到故事板中的视图控制器。我认为这比用代码设置要好

  3. 在视图控制器中实现该方法:

     textField(_:shouldChangeCharactersInRange:replacementString:)
    

I think an extension is more handy for this. See the full answer 给你.

private var maxLengths = [UITextField: Int]()


// 2
extension UITextField {


// 3
@IBInspectable var maxLength: Int {
get {
// 4
guard let length = maxLengths[self] else {
return Int.max
}
return length
}
set {
maxLengths[self] = newValue
// 5
addTarget(
self,
action: #selector(limitLength),
forControlEvents: UIControlEvents.EditingChanged
)
}
}


func limitLength(textField: UITextField) {
// 6
guard let prospectiveText = textField.text
where prospectiveText.characters.count > maxLength else {
return
}


let selection = selectedTextRange
// 7
text = prospectiveText.substringWithRange(
Range<String.Index>(prospectiveText.startIndex ..< prospectiveText.startIndex.advancedBy(maxLength))
)
selectedTextRange = selection
}


}

和 Steven Schmatz 一样,但是使用了 Swift 3.0:

//max Length
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange,
replacementString string: String) -> Bool
{
let maxLength = 4
let currentString: NSString = textField.text! as NSString
let newString: NSString = currentString.replacingCharacters(in: range, with: string) as NSString
return newString.length <= maxLength
}

Modern Swift

Note that 关于这个问题的大部分在线示例代码都已经过时了.

将以下内容粘贴到项目中的任何 Swift 文件中(您可以将该文件命名为任何名称,例如,“ Handy.Swift”。)

这最终解决了 iOS 中最愚蠢的问题之一:

enter image description here

您的文本字段现在有一个 .maxLength

完全可以在开发期间在故事板中设置该值,或者在应用程序运行时在代码中设置该值。

// simply have this in any Swift file, say, Handy.swift


import UIKit
private var __maxLengths = [UITextField: Int]()
extension UITextField {
@IBInspectable var maxLength: Int {
get {
guard let l = __maxLengths[self] else {
return 150 // (global default-limit. or just, Int.max)
}
return l
}
set {
__maxLengths[self] = newValue
addTarget(self, action: #selector(fix), for: .editingChanged)
}
}
func fix(textField: UITextField) {
let t = textField.text
textField.text = t?.prefix(maxLength).string
}
}

就这么简单。


脚注-这些天来,安全截断 String在迅速,你只是 .prefix(n)


一个更简单的一次性版本。

以上修复了项目中的 所有文本字段。

If you just want 有一个特别的 text field to simply be limited to say "4", and that's that...

class PinCodeEntry: UITextField {
    

override func didMoveToSuperview() {
        

super.didMoveToSuperview()
addTarget(self, action: #selector(fixMe), for: .editingChanged)
}
    

@objc private func fixMe() { text = text?.prefix(4) }
}

呼! 就是这样。

(顺便说一句,这里有一个类似的非常有用的技巧与 UIText观景有关, Https://stackoverflow.com/a/42333832/294884


对于强迫症程序员(像我一样) ..。

正如@LeoDabus 提醒的那样,.prefix返回一个子字符串

let t = textField.text
textField.text = t?.prefix(maxLength)

就是

if let t: String = textField.text {
textField.text = String(t.prefix(maxLength))
}

好好享受吧!

Here's a Swift 3.2+ alternative that avoids unnecessary string manipulation. In this case, the maximum length is 10:

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


return text.count - range.length + string.count <= 10
}

This answer is for Swift 4 and is pretty straightforward with the ability to let backspace through.

func textField(_ textField: UITextField,
shouldChangeCharactersIn range: NSRange,
replacementString string: String) -> Bool {
return textField.text!.count < 10 || string == ""
}

我根据@Frouo 给出一个补充答案。我认为他的回答是最美丽的方式。因为这是我们可以重用的常用控件。这里没有任何泄漏问题。

private var kAssociationKeyMaxLength: Int = 0


extension UITextField {


@IBInspectable var maxLength: Int {
get {
if let length = objc_getAssociatedObject(self, &kAssociationKeyMaxLength) as? Int {
return length
} else {
return Int.max
}
}
set {
objc_setAssociatedObject(self, &kAssociationKeyMaxLength, newValue, .OBJC_ASSOCIATION_RETAIN)
self.addTarget(self, action: #selector(checkMaxLength), for: .editingChanged)
}
}


// The method is used to cancel the check when using
// the Chinese Pinyin input method.
// Becuase the alphabet also appears in the textfield
// when inputting, we should cancel the check.
func isInputMethod() -> Bool {
if let positionRange = self.markedTextRange {
if let _ = self.position(from: positionRange.start, offset: 0) {
return true
}
}
return false
}




func checkMaxLength(textField: UITextField) {


guard !self.isInputMethod(), let prospectiveText = self.text,
prospectiveText.count > maxLength
else {
return
}


let selection = selectedTextRange
let maxCharIndex = prospectiveText.index(prospectiveText.startIndex, offsetBy: maxLength)
text = prospectiveText.substring(to: maxCharIndex)
selectedTextRange = selection
}


}

之前发布的其他解决方案由于文本字段映射而产生一个保留循环。此外,如果没有设置 maxLength属性而不是人为的 Int.max构造,那么 maxLength属性应该是可空的; 如果 maxLlength 被更改,那么目标将被设置多次。

这是 更新了 Swift4的解决方案,带有弱映射,以防止内存泄漏和其他修复

private var maxLengths = NSMapTable<UITextField, NSNumber>(keyOptions: NSPointerFunctions.Options.weakMemory, valueOptions: NSPointerFunctions.Options.strongMemory)


extension UITextField {


var maxLength: Int? {
get {
return maxLengths.object(forKey: self)?.intValue
}
set {
removeTarget(self, action: #selector(limitLength), for: .editingChanged)
if let newValue = newValue {
maxLengths.setObject(NSNumber(value: newValue), forKey: self)
addTarget(self, action: #selector(limitLength), for: .editingChanged)
} else {
maxLengths.removeObject(forKey: self)
}
}
}


@IBInspectable var maxLengthInspectable: Int {
get {
return maxLength ?? Int.max
}
set {
maxLength = newValue
}
}


@objc private func limitLength(_ textField: UITextField) {
guard let maxLength = maxLength, let prospectiveText = textField.text, prospectiveText.count > maxLength else {
return
}
let selection = selectedTextRange
text = String(prospectiveText[..<prospectiveText.index(from: maxLength)])
selectedTextRange = selection
}
}

这是在 Swift 4中工作的

Step 1: Set UITextFieldDelegate

class SignUPViewController: UIViewController , UITextFieldDelegate {


@IBOutlet weak var userMobileNoTextFiled: UITextField!


override func viewDidLoad() {
super.viewDidLoad()

步骤2: 设置委托

        userMobileNoTextFiled.delegate = self                  // Set delegate
}


func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
//        guard let text = userMobileNoTextFiled.text else { return true }
//        let newLength = text.count + string.count - range.length
//        return newLength <= 10
//    }

步骤3: 调用函数

    func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let maxLength = 10          // Set your need
let currentString: NSString = textField.text! as NSString
let newString: NSString =
currentString.replacingCharacters(in: range, with: string) as NSString
return newString.length <= maxLength
}
}

在 Swift 4中,简单地使用:

public func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
return range.location < 10
}

不使用委托的简单解决方案:

TEXT_FIELD.addTarget(self, action: #selector(editingChanged(sender:)), for: .editingChanged)




@objc private func editingChanged(sender: UITextField) {


if let text = sender.text, text.count >= MAX_LENGHT {
sender.text = String(text.dropLast(text.count - MAX_LENGHT))
return
}
}

我的 Swift 4版 shouldChangeCharactersIn

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


guard let preText = textField.text as NSString?,
preText.replacingCharacters(in: range, with: string).count <= MAX_TEXT_LENGTH else {
return false
}


return true
}

胖子的回答的最新情况:

extension UITextField {


// Runtime key
private struct AssociatedKeys {
// Maximum length key
static var maxlength: UInt8 = 0
// Temporary string key
static var tempString: UInt8 = 0
}


// Limit the maximum input length of the textfiled
@IBInspectable var maxLength: Int {
get {
return objc_getAssociatedObject(self, &AssociatedKeys.maxlength) as? Int ?? 0
}
set {
objc_setAssociatedObject(self, &AssociatedKeys.maxlength, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
addTarget(self, action: #selector(handleEditingChanged(textField:)), for: .editingChanged)
}
}


// Temporary string
private var tempString: String? {
get {
return objc_getAssociatedObject(self, &AssociatedKeys.tempString) as? String
}
set {
objc_setAssociatedObject(self, &AssociatedKeys.tempString, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}


// When the text changes, process the amount of text in the input
// box so that its length is within the controllable range.
@objc private func handleEditingChanged(textField: UITextField) {


// Special processing for the Chinese input method
guard markedTextRange == nil else { return }


if textField.text?.count == maxLength {


// Set lastQualifiedString where text length == maximum length
tempString = textField.text
} else if textField.text?.count ?? 0 < maxLength {


// Clear lastQualifiedString when text length > maxlength
tempString = nil
}


// Keep the current text range in arcgives
let archivesEditRange: UITextRange?


if textField.text?.count ?? 0 > maxLength {


// If text length > maximum length, remove last range and to move to -1 postion.
let position = textField.position(from: safeTextPosition(selectedTextRange?.start), offset: -1) ?? textField.endOfDocument
archivesEditRange = textField.textRange(from: safeTextPosition(position), to: safeTextPosition(position))
} else {


// Just set current select text range
archivesEditRange = selectedTextRange
}


// Main handle string maximum length
textField.text = tempString ?? String((textField.text ?? "").prefix(maxLength))


// Last configuration edit text range
textField.selectedTextRange = archivesEditRange
}


// Get safe textPosition
private func safeTextPosition(_ optionlTextPosition: UITextPosition?) -> UITextPosition {


/* beginningOfDocument -> The end of the the text document. */
return optionlTextPosition ?? endOfDocument
}
}

我使用这些步骤。首先在 viewdiload 中设置委托文本字段。

override func viewDidLoad() {
super.viewDidLoad()


textfield.delegate = self
}

然后应该在包含 UITextField 委托之后更改字符。

extension viewController: UITextFieldDelegate {
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let newLength = (textField.text?.utf16.count)! + string.utf16.count - range.length
if newLength <= 8 {
return true
}
else {
return false
}
}
}

只需检查字符串中的字符数即可

  1. 向视图控制器添加委托并分配委托

    class YorsClassName : UITextFieldDelegate {
    
    
    }
    
  2. 检查文本字段允许的字符数

    func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
    if textField.text?.count == 1 {
    return false
    }
    return true
    }
    

Note: Here I checked for only characters allowed in textField.

For Swift 5:

只需写一行来设置最大字符长度:

 self.textField.maxLength = 10

For more details, see 最大字符限制 UITextField 和允许字符 Swift. (Also credited.)

阻止 Swift 4中的文本后的 TextField 限制字符

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




if textField == self.txtDescription {
let maxLength = 200
let currentString: NSString = textField.text! as NSString
let newString: NSString = currentString.replacingCharacters(in: range, with: string) as NSString
return newString.length <= maxLength
}


return true




}

以防万一,在将范围大小应用到字符串之前,不要忘记保护它。否则,如果用户执行以下操作,就会崩溃:

  • 键入最大长度文本
  • 插入一些东西(由于长度限制,不会插入任何东西,但是 iOS 不知道)
  • 撤消插入操作(会出现崩溃,因为范围将大于实际的字符串大小)

Also, using iOS 13 users can accidentally trigger this by gestures

我建议你在你的项目中加入这个

extension String {
func replace(with text: String, in range: NSRange) -> String? {
// NOTE: NSString conversion is necessary to operate in the same symbol steps
// Otherwise, you may not be able to delete an emoji, for example
let current = NSString(string: self)
guard range.location + range.length <= current.length else { return nil }
return current.replacingCharacters(in: range, with: text)
}
}

像这样使用它:

func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
guard let newText = textView.text.replace(with: text, in: range) else { return false }
return newText.count < maxNumberOfCharacters
// NOTE: You may wanna trim the new text instead,
// so the user will able to shove his long text at least partially
}

否则,你的应用程序会不断崩溃。

如果在一个页面上有多个 textField,它们具有不同的长度检查,那么我找到了一个简单而简短的解决方案。

class MultipleTextField: UIViewController {


let MAX_LENGTH_TEXTFIELD_A = 10
let MAX_LENGTH_TEXTFIELD_B = 11


lazy var textFieldA: UITextField = {
let textField = UITextField()
textField.tag = MAX_LENGTH_TEXTFIELD_A
textField.delegate = self
return textField
}()
lazy var textFieldB: UITextField = {
let textField = UITextField()
textField.tag = MAX_LENGTH_TEXTFIELD_B
textField.delegate = self
return textField
}()
}


extension MultipleTextField: UITextFieldDelegate {
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
return (range.location < textField.tag) && (string.count < textField.tag)
}
}
  1. 设置 textfield 的委托:

     textField.delegate = self
    
  2. 在视图控制器中实现该方法:

     // MARK: Text field delegate
    
    
    extension ViewController: UITextFieldDelegate {
    func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
    return range.location < maxLength (maxLength can be any maximum length you can define)
    }
    }
    
lazy var textField: UITextField = {
let textField = UITextField()
textField.addTarget(self, #selector(handleOnEditing), for .editingChanged)
return textField
}()

//在 ViewDidLoad 中设置委托 分派 = 自我

@objc func handleOnEditing() {
let text = textField.text ?? ""
let limit = 10
textField.text = String(text.prefix(limit))
}