[TIL] Floating Placeholder

박주하·2025년 7월 18일

Floating Placeholder


  • 입력창에 표시되는 placeholder가, 텍스트를 입력하면 위로 작아지며 떠오르는 효과
  1. 아무것도 입력하지 않으면 placeholder가 텍스트필드 안 중앙에 있음
  2. 사용자가 텍스트를 입력하거나 포커스가 생기면 placeholder가 작아지며 위로 이동 (위쪽 label처럼 보여짐)
  3. 텍스트가 사라지고 포커스도 없으면 다시 제자리로 내려옴

왜 사용할까?

  • 사용자 경험 향상: 입력 필드가 어떤 의미인지 계속 볼 수 있음
  • 공간 절약: 별도로 라벨을 만들지 않아도 됨

구현 방법

  1. UILabel + UITextField 조합
  • UILabel을 placeholder로 사용
  • UITextField에 포커스 또는 입력 여부에 따라 UILabel 위치 및 크기 조절
  1. 애니메이션 활용 (transform, alpha, constraints)

class FloatingTextField: UIView {
    private let placeholderLabel = UILabel()
    let textField = UITextField()

    private var isFloating = false

    override init(frame: CGRect) {
        super.init(frame: frame)
        setup()
    }

    required init?(coder: NSCoder) {
        fatalError()
    }

    private func setup() {
        // 텍스트 필드
        textField.borderStyle = .roundedRect
        textField.addTarget(self, action: #selector(textChanged), for: .editingChanged)
        textField.delegate = self

        // 플레이스홀더
        placeholderLabel.text = "이메일"
        placeholderLabel.font = UIFont.systemFont(ofSize: 16)
        placeholderLabel.textColor = .lightGray
        placeholderLabel.translatesAutoresizingMaskIntoConstraints = false

        addSubview(textField)
        addSubview(placeholderLabel)

        textField.translatesAutoresizingMaskIntoConstraints = false

        NSLayoutConstraint.activate([
            placeholderLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 8),
            placeholderLabel.topAnchor.constraint(equalTo: topAnchor, constant: 18),
            
            textField.topAnchor.constraint(equalTo: topAnchor),
            textField.leadingAnchor.constraint(equalTo: leadingAnchor),
            textField.trailingAnchor.constraint(equalTo: trailingAnchor),
            textField.bottomAnchor.constraint(equalTo: bottomAnchor)
        ])
    }

    @objc private func textChanged() {
        updatePlaceholder()
    }

    private func updatePlaceholder() {
        let shouldFloat = !(textField.text?.isEmpty ?? true) || textField.isFirstResponder

        guard shouldFloat != isFloating else { return }

        isFloating = shouldFloat
        UIView.animate(withDuration: 0.25) {
        	self.placeholderLabel.transform = isFloating ? CGAffineTransform(translationX: -5, y: -15).scaledBy(x: 0.75, y: 0.75) : .identity
        }
    }
}

extension FloatingTextField: UITextFieldDelegate {
    func textFieldDidBeginEditing(_ textField: UITextField) {
        updatePlaceholder()
    }

    func textFieldDidEndEditing(_ textField: UITextField) {
        updatePlaceholder()
    }
}

0개의 댓글