Intro

UITextField 를 아이디나 비밀번호를 사용자로부터 입력받을 때 사용하게되면 글자수 제한을 두어야하는 경우가 있는데요. 이 때 글자수 입력을 제한할 수 있는 방법에 대해 알아보도록 하겠습니다.


Prerequisite

테스트를 위해 새로운 프로젝트를 하나 생성하고 UITextField 하나를 View 의 중앙에 올려보았습니다.

import UIKit

class ViewController: UIViewController {

    private var textField = UITextField()

    override func viewDidLoad() {
        super.viewDidLoad()
        configureUI()
    }

    // MARK: - UI
    private func configureUI() {
        setContraints()
        setAttributes()
    }

    private func setAttributes() {
        textField.layer.cornerRadius = 4
        textField.layer.borderWidth = 1
        textField.layer.borderColor = UIColor.lightGray.cgColor
        textField.placeholder = "  Type your text here"
    }

    private func setContraints() {
        view.addSubview(textField)
        textField.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            textField.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            textField.centerYAnchor.constraint(equalTo: view.centerYAnchor),
            textField.heightAnchor.constraint(equalToConstant: 50),
            textField.widthAnchor.constraint(equalToConstant: 300),
        ])
    }
}

이제 Simulator 를 실행해보면 텍스트 필드가 중앙에 보일거에요!


UITextFieldDelegate 채택하기

프로그래밍이 언제나 그렇듯이 이 기능을 구현하는 방법도 여러가지가 있습니다만, 오늘은 UITextFieldDelegate 를 채택하고 textField(_:shouldChangeCharactersIn:replacementString:) method 를 호출해 해결을 해보도록 할게요.

textField(_:shouldChangeCharactersIn:replacementString:) method 는 TextField 의 값이 변할 때마다 호출되는 함수이며, 리턴값으로 Bool 타입을 가집니다. 리턴값이 true 일 경우 사용자가 입력한 텍스트가 TextField 에 입력되고 false 일 경우 입력을 무시하게 됩니다. 그럼 여기에 지속적으로 텍스트를 감시할 수 있는 코드를 구현하고 텍스트가 몇 자 이상일 때 값이 입력되지 않도록 구현해주면 되겠죠?

먼저 UITextFieldDelegate 을 구현해볼게요.

extension ViewController: UITextFieldDelegate {
    func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
        return true
    }
}

Delegate 위임도 잊지말고요!

textField.delegate = self

이제 textField(_:shouldChangeCharactersIn:replacementString:) 내부 코드를 구현해보겠습니다. 텍스트가 최대 5 자 까지만 입력될 수 있도록 해볼게요. 코드 단 한줄이면 됩니다.

guard textField.text!.count < 5 else { return false }

이제 텍스트필드에 입력을 해보면 5 자 이상은 입력이 되지않을거에요 ㅎㅎ


Backspace Event 감지하기

단 이렇게만 세팅을 해두면 문제점이 하나 생기는데요. 바로 5 글자를 입력한 이후부터는 그 사용자는 그 어떤 행동도 할 수가 없게됩니다. 최소한 글을 다시 수정할 수는 있어야 하잖아요? ㅎㅎ

그래서 이번에는 Backspace 가 입력되었을 때는 글자를 삭제할 수 있도록 textField(_:shouldChangeCharactersIn:replacementString:) 내부에 코드를 추가해볼게요. 조금 전 작성했던 guard 문 위에 입력하면 됩니다.

if let char = string.cString(using: String.Encoding.utf8) {
    let isBackSpace = strcmp(char, "\\b")
    if isBackSpace == -92 {
        return true
    }
}

으음… 글쎄요. 이 코드는 저도 완전하게 이해하고 있는 부분이 아니라서 간단하게만 설명드리자면 string 은 호출되는 함수의 Parameter 로 들어오는 값이고, 사용자가 입력 시도한 값이 실제 입력여부와 관계없이 계속 들어오게 됩니다. 그리고 이렇게 들어오는 값을 Backspace 인지 판단하고 인코딩하여 UInt32 의 형태로 변환하는 과정입니다.

코드의 2 번째 줄을 통해 사용자가 키보드에 입력하는 값을 문자 형태로 받을 수 있게되고, Backspace 의 경우에는 \\b 의 문자를 가집니다. Backspace 가 입력될 경우 UInt32 의 형태로 변환하게 되며, 이 값은 -92 라는 값을 가지므로 이 조건들을 모두 만족했을 경우에는 사용자의 입력이 실행되도록 리턴값을 수정해 주었습니다.

여기까지 코드를 입력하고 다시 Simulator 를 실행해보면 조금 전까지는 5 자 이상 입력 시부터 지울 수 없던 글자들이 잘 지워지는 것을 확인할 수 있습니다.


Wrap Up

간단하면서도 자주 사용하지 않으면 잊어버리기 쉬운 내용입니다 ㅎㅎ 그래서 저는 이렇게 기록으로 남겨두고 필요할 때마다 빠르게 찾아쓰는 방향으로 하려고요. 아무튼 도움이 되셨길 바랍니다!

import UIKit

class ViewController: UIViewController {

    private var textField = UITextField()

    override func viewDidLoad() {
        super.viewDidLoad()
        configureUI()
    }

    // MARK: - UI
    private func configureUI() {
        setContraints()
        setAttributes()
    }

    private func setAttributes() {
        textField.layer.cornerRadius = 4
        textField.layer.borderWidth = 1
        textField.layer.borderColor = UIColor.lightGray.cgColor
        textField.placeholder = "  Type your text here"

        textField.delegate = self
    }

    private func setContraints() {
        view.addSubview(textField)
        textField.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            textField.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            textField.centerYAnchor.constraint(equalTo: view.centerYAnchor),
            textField.heightAnchor.constraint(equalToConstant: 50),
            textField.widthAnchor.constraint(equalToConstant: 300),
        ])
    }
}

extension ViewController: UITextFieldDelegate {
    func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
        print(string)
        if let char = string.cString(using: String.Encoding.utf8) {
            let isBackSpace = strcmp(char, "\\b")
            if isBackSpace == -92 {
                return true
            }
        }
        guard textField.text!.count < 5 else { return false }
        return true
    }
}