iOS 팀 프로젝트/소분해요

(RightView)TextField에 Extension 추가하기

seongpil Heo 2026. 1. 4. 02:05
728x90

  👨🏻‍💻  오늘의 작업

[ 1. TextField에 Extension 추가하기 ] 

 

정산 1단계 상품 등록하기 부분에서 단위 입력과 금액 입력 TextField에 사용자가 값을 입력할 때

정수만 입력이 가능하도록 입력을 제한해야 한다.

 

해야 하는 것은 2가지 정도 된다.

 

사용자에게 입력을 받을 때 키보드 타입을 숫자패드로 변경하여 숫자만 입력할 수 있도록 유도한다.

그다음 TextField에 Extension을 추가해서 입력된 값이 Int값인지 검증하고 Int값이면 표시,

Int값이 아니면 입력되지 않게 처리하는 방법이 있다.

 

그러나 키보드 타입만 제한하는 방법에는 문제점이 있다.

바로 사용자가 복사 / 붙여 넣기 기능을 사용할 때이다.

외부에서 값을 복사 후 앱에서 붙여 넣기를 사용하면 키보드 타입을 넘버패드로 제한하여도 Int값이 아닌 String값이나

다른 값을 입력할 수 있는 문제가 있다.

 

해당 문제를 해결하기 위해 Extension까지 추가하는 방법이다.

나의 경우에는 RightViewTextField 컴포넌트에서만 사용할 예정이기 때문에

RightViewTextField에 Extension을 추가했다.

 

[ 1. 키보드 타입을 제한하기 ]

final class RightViewTextField: PaddedTextField {
    init(rightText: String, keyboardType: UIKeyboardType = .numberPad) {
        super.init(frame: .zero, fontStyle: body16)
        self.keyboardType = keyboardType // 추가된 부분
        configureUI(rightText: rightText)
    }

 

 

[ 2. TextField에 Extension 추가하기 ]

extension Reactive where Base: RightViewTextField {
    /// 천 단위 콤마가 포함된 숫자 텍스트
    var formattedNumericText: ControlProperty<String> {
        let formatter = NumberFormatter()
        formatter.numberStyle = .decimal
        formatter.groupingSeparator = ","
        formatter.maximumFractionDigits = 0
        
        let source = base.rx.text.orEmpty
            .map { [weak base] text -> String in
                guard let base = base else { return "" }

                let numbers = text.filter { $0.isNumber }

                guard !numbers.isEmpty, let value = Int(numbers) else {
                    if base.text != "" {
                        base.text = ""
                    }
                    
                    return ""
                }

                let formatted = formatter.string(from: NSNumber(value: value)) ?? numbers
                
                if base.text != formatted {
                    base.text = formatted
                }
                
                return numbers
            }
        
        let observer = Binder<String>(base) { textField, text in
            let numbers = text.filter { $0.isNumber }
            guard !numbers.isEmpty, let value = Int(numbers) else {
                if textField.text != "" {
                    textField.text = ""
                }
                
                return
            }
            
            let formatted = formatter.string(from: NSNumber(value: value)) ?? numbers
            if textField.text != formatted {
                textField.text = formatted
            }
        }
        
        return ControlProperty(values: source, valueSink: observer)
    }
}

 

View에서 사용하기
// 등록하기 버튼 활성화 여부
Observable.combineLatest(
    itemNameTextField.rx.text.orEmpty,
    itemCountTextField.rx.formattedNumericText, // Extension 사용
    itemAmountTextField.rx.formattedNumericText // Extension 사용
)
.map { !$0.0.isEmpty && !$0.1.isEmpty && !$0.2.isEmpty }
.bind(to: registerButton.rx.isEnabled)
.disposed(by: disposeBag)

// 등록하기 버튼 탭
registerButton.rx.tap
    .withLatestFrom(
        Observable.combineLatest(
            itemNameTextField.rx.text.orEmpty,
            itemCountTextField.rx.formattedNumericText, // Extension 사용
            itemAmountTextField.rx.formattedNumericText // Extension 사용
        )
    )
    .map { name, count, amount in
        Reactor.Action.registerButtonTapped(name: name, count: count, amount: amount)
    }
    .bind(to: reactor.action)
    .disposed(by: disposeBag)

 

 

728x90