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


85

我知道还有其他主题,但似乎无法找到实现方法。

我试图将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”



快速提示-以缩短String斯威夫特这些天你终于可以只.PREFIX(N)
Fattie

Answers:


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

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

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

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
}

如何对仅包含最大长度的数字输入的iOS文本字段进行编程


非常感谢您的及时答复!如果我将此文本字段设置为委托,我是否可以修改其他文本字段?
ishkur88

是的,你会得到有问题的文本框(编辑中)作为第一个参数textField的方法 func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool
阿拉丁

但是我应该在第二个参数中输入什么呢?将其设置为委托后,我不再引用myTextField。
ishkur88

就像我只想为电话号码创建另一个文本字段0-9一样。
ishkur88

每次编辑文本字段时,shouldChangeCharactersInRange都会调用该回调,这适用于所有文本字段,您会在同一位置收到回调,shouldChangeCharactersInRange并且在此方法内,您可以知道正在编辑哪个文本字段,这要归功于传递的参数textField,例如,您可以给每个文本字段的标记,并在和测试shouldChangeCharactersInRange每个文本字段以执行内容验证
Aladin

113

2017年12月。Swift4。

请注意,有关此问题的许多示例代码都已经过时

将以下内容粘贴到项目中的任何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,您可以快速安全地截断.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) }
}

这里的所有都是它的。

(顺便说一句,这是与UIText View有关的类似的非常有用的技巧, https: //stackoverflow.com/a/42333832/294884 )


对于OCD程序员(像我一样)...

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

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

将会

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

请享用!


2
似乎是合法的!谢谢。
J. Doe

2
是否有这样的事情TextView?
多莉(Dory)

13
必须完成所有这些工作才能实现如此普遍且如此简单的功能,这让我震惊。他们不能只给我们内置一个简单的textField.maxLength ...反正您的解决方案很棒,谢谢!
mylovemhz

2
解决方案很方便,但是顶部的文本字段图实际上会产生一个保留周期。
Angel G. Olloqui '18 -4-4

3
警告使用此解决方案!该解决方案存在一些问题。其中之一是,如果用户在textField的开头键入内容,则将允许他们键入新字符,并且将删除最后一个字符,并且光标将跳至字段中的最后一个字符。另一个问题是,如果以编程方式设置文本,它将允许您设置大于限制的文本。如果您撤消更改(使用外部键盘的CMD + Z),则会发生另一个问题,如果您尝试添加一个超出限制的数字,它将崩溃。
juancazalla

26

Swift 4,只需使用:

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

这应该是选定的答案。简单且运作良好。
user832 '18

9
它不起作用。如果点击字符串的中间并且可以键入多个X字符,该怎么办。
Slavcho '18

可以使用textField.text.length <10代替range.location <10。此解决方案简单而优雅。
萨奇布·沙特

您可以使用此解决方案:如果的TextField.text .Count之间> = 12 {将返回false}?
СергейБилык

1
如果要在过去的操作中进行操作,则在输入文本string.count < MAX_LENGTH
后将不起作用

12

史蒂文·施马茨(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
}

1
好答案。感激。
哈西亚

谢谢@Hasya
Pavlos


6

我认为扩展对此更方便。在这里查看完整答案

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
  }

}

4

上面发布的其他解决方案由于文本字段映射而产生保留周期。此外,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
    }
}

感谢您的回答,您能解释一下maxLengths吗?
Keyhan Kamangar '18

3

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

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

2
我正在寻找的答案:D
安库·拉希尔

1
这是解决方案,简单而优雅,没有样板代码。
zeeshan

3

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

这应该是公认的答案。完美运行,没有错误。
10623169

2

我对阿拉丁的回答有补充:

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

    class MyViewController: UIViewController, UITextViewDelegate {
    
    }
    
  2. 设置文本字段的委托:要设置委托,您可以控制从文本字段拖到情节提要中的视图控制器。我认为这比在代码中设置它更可取

  3. 在您的视图控制器中实现该方法: textField(_:shouldChangeCharactersInRange:replacementString:)


2

我根据@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 use 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
        }



    }

2

更新此Fattie答案

谢谢

extension UITextField {

    /// Runtime key
    private struct AssociatedKeys {
        /// max lenght key
        static var maxlength: UInt8 = 0
        /// temp 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)
        }
    }

    /// temp 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 Chinese Input Method
        guard markedTextRange == nil else { return }

        if textField.text?.count == maxLength {

            /// SET lastQualifiedString where text length == max lenght
            tempString = textField.text
        } else if textField.text?.count ?? 0 < maxLength {

            /// clear lastQualifiedString when text lengeht > maxlength
            tempString = nil
        }

        /// keep current text range in arcgives
        let archivesEditRange: UITextRange?

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

            /// if text length > maxlength,remove last range,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 max length
        textField.text = tempString ?? String((textField.text ?? "").prefix(maxLength))

        /// last config 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
    }
}

1

在Swift4中工作

//步骤1设置UITextFieldDelegate

    class SignUPViewController: UIViewController , UITextFieldDelegate {

       @IBOutlet weak var userMobileNoTextFiled: UITextField!

        override func viewDidLoad() {
            super.viewDidLoad()

// STEP 2设置委托
userMobileNoTextFiled.delegate = self //设置委托}

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

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

1

这个答案是针对Swift 4的,并且可以让退格键直截了当。

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

这不处理复制和粘贴
cornr

1

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

  1. 添加委托以查看控制器anssign委托
    class YorsClassName : UITextFieldDelegate {

    }
  1. 检查文本字段允许的字符数
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
    if textField.text?.count == 1 {
        return false
    }
    return true
}

注意:这里我只检查了textField中允许的char


1

在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


}

1

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

输入最大长度的文本插入内容(由于长度限制,将不会插入任何内容,但iOS对此一无所知)撤消插入操作(您会崩溃,原因是范围大于实际的字符串大小)

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

我建议您将此添加到您的项目中

extension String {
    func replace(with text: String, in range: NSRange) -> String? {
        guard range.location + range.length <= self.count else { return nil }
        return (self as NSString).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
}

否则,您将不断崩溃于应用程序中


0

这是一个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
}

0

我使用此步骤,首先在viewdidload中设置委托texfield。

    override func viewDidLoad() {
        super.viewDidLoad()

        textfield.delegate = self

    }

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

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

0

如果您有多个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)
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.