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

Set the maximum character length of a UITextField in Swift

我知道还有关于此的其他主题,但我似乎无法找到如何实现它。

我试图将 UITextField 限制为仅五个字符。

最好是字母数字,-._

我看过这段代码:

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 换掉哪个“文本字段”?

  1. 您的视图控制器应符合 UITextFieldDelegate,如下所示:

    class MyViewController: UIViewController, UITextFieldDelegate {
    
    }
    
  2. 设置您的文本字段的委托: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
}

为Swift4

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
}

为Swift5

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 == numberField {
        if count(string) > 0 {
            let disallowedCharacterSet = NSCharacterSet(charactersInString: "0123456789.-").invertedSet
            let replacementStringIsLegal = string.rangeOfCharacterFromSet(disallowedCharacterSet) == nil
            result = replacementStringIsLegal
        }
    }

    return result
}

How to program an iOS text field that takes only numeric input with a maximum length

我有东西要补充 :

  1. 你的视图控制器应该符合UITextFieldDelegate

     class MyViewController: UIViewController, UITextViewDelegate {
    
     }
    
  2. 设置您的文本字段的委托:

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

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

     textField(_:shouldChangeCharactersInRange:replacementString:)
    

我认为扩展程序对此更为方便。查看完整答案 here

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
}

现代Swift

请注意,许多关于此问题的在线示例代码已经过时

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

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

您的文本字段现在有一个 .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
    }
}

就这么简单。


脚注 - 现在要安全地截断 swift 中的 String,您只需 .prefix(n)


更简单的一次性版本...

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

如果您只想一个特定的 文本字段只限于说“4”,那就是...

class PinCodeEntry: UITextField {
    
    override func didMoveToSuperview() {
        
        super.didMoveToSuperview()
        addTarget(self, action: #selector(fixMe), for: .editingChanged)
    }
    
    @objc private func fixMe() { text = text?.prefix(4) }
}

呸! 仅此而已。

(顺便说一句,这里有一个与 UITextView 相关的非常有用的提示, )


对于强迫症程序员(比如我)...

正如@LeoDabus 提醒的那样,.prefix returns 一个子字符串。如果您非常关心他人,这

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

会是

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

尽情享受吧!

这是一个 Swift 3.2+ 替代方案,可避免不必要的字符串操作。在这种情况下,最大长度为 10:

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

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

这个答案是针对 Swift 4 的,并且非常简单,可以让退格键通过。

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,目标将被多次设置。

这里是 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

中有效

第 1 步:设置 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
    }
}

在Swift4中,只需使用:

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
        }
}

我的Swift4版本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
    }
}

我使用这些步骤。首先在viewdidload中设置delegate text field.

override func viewDidLoad() {
    super.viewDidLoad()

    textfield.delegate = self
}

然后在包含 UITextFieldDelegate 之后应该使用 shouldChangeCharactersIn。

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
    }
    

注意:这里我只检查了 textField.

中允许的字符

对于Swift 5:

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

 self.textField.maxLength = 10

详情请见Max character limit of UITextField and allowed characters Swift。 (也记入了。)

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不知道)
  • 撤消插入(你会崩溃,因为范围将大于实际字符串大小)

此外,使用 iOS 13 个用户可能会意外地通过手势触发此操作

我建议你把这个添加到你的项目中

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.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)
         }
     }