개발하자 중엽아
  • isSecureTextEntry 활성화로 인하여 발생하는 입력값 초기화
    2025년 02월 10일 16시 02분 14초에 업로드 된 글입니다.
    작성자: 이중엽

    개요

    textField의 isSecureTextEntry를 활성화해주면 비밀번호와 같이 숨김 처리를 해야하는 정보들이 알아서 가림 처리가 된다.

    그래서 보통 비밀번호 TextField는 위 프로퍼티를 꼭 활성화시켜주곤 한다.

     

    다만 isSecureTextEntry를 활성화하면, TextField가 resigned된 이 후 다시 response가 발생했을 때 자동으로 입력값을 초기화시켜버린다.

     

    위 영상처럼 해당 텍스트 필드를 벗어난 후 다시 입력할 때 다시금 입력해야하는 불편함이 발생했다.

    그리고 이게 그렇게 자연스럽게 느껴지지 않는다.

     

    해결 방법

    솔직히 간단하게 끝날 줄 알았는데, 생각보다 오래걸렸다.

    private var isEditingDidEnd: Bool = false
    let passwordStream = passwordTextField.rx.text.orEmpty
    .scan(("", "")) { previous, newText in
    return (previous.1, newText)
    }
    .share()
    let inputStream = passwordStream.filter { $0.count < $1.count }
    let deleteStream = passwordStream.filter { $0.count > $1.count }
    // 일반 입력
    inputStream
    .bind(with: self) { owner, data in
    let (previous, newText) = data
    owner.isEditingDidEnd = false
    if previous == String() {
    let lastInput = owner.viewModel.fetchInputPassword()
    owner.passwordTextField.text = lastInput + newText
    owner.viewModel.inputPasswordEvent.accept(lastInput + newText)
    } else {
    owner.viewModel.input.inputPasswordEvent.accept(newText)
    }
    }
    .disposed(by: disposeBag)
    // 백스페이스
    deleteStream
    .map { previous, newText in
    return newText
    }
    .bind(with: self) { owner, newText in
    if owner.isEditingDidEnd, newText.count == Int() { return }
    owner.viewModel.input.inputPasswordEvent.accept(newText)
    }
    .disposed(by: disposeBag)
    // 편집 완료
    passwordTextField.rx.controlEvent(.editingDidEnd)
    .bind(with: self) { owner, _ in
    owner.isEditingDidEnd = true
    }
    .disposed(by: disposeBag)

     

    우선 scan과 share 그리고 filter를 통해 inputStream과 deleteStream을 구분해주었다.

     

    여기서 또 한가지 문제는 초기화가 될 때도 deleteStream으로 값이 넘어왔다.

    초기화가 되면서 previous보다 newText가 작았기 때문이다.

     

    이러면 backSpcace와 초기화로 인한 차이를 구분할 수가 없다.

    그래서 controlEvent로 editing이 끝나는 이벤트가 발생할 때 만들어둔 변수 isEditingDidEnd를 true로 바꿔줌으로써 이를 활용하여 backSpace와 초기화를 구분할 수 있게 되었다.

     


     

    input에서는 isEditingDidEnd를 입력이 발생하면 false로 바꾸어준다.

    output에서는 isEditingDidEnd일때는 isSecureTextEntry로 인한 초기화이기 때문에 해당 이벤트를 무시한다.

     

    순서 상 input이 먼저 false로 바꿔버려서 output이 동작할 때 그대로 통과하면 어떡하냐 할 수있지만,

    input과 delete는 share에서 분기된 stream이기 때문에 순서와 상관없이 동일한 이벤트를 전달받는다.

     

    마지막으로 input에서는 초기화 이벤트를 감지하지 못하기 때문에, previous가 String() 즉 초기화되어있을 때 마지막 입력값을 가져와서 새로운 입력과 합쳐준다.


    여기서 text에 다시 주입하면 결국 다시 rx.text가 실행되어서 무한루프에 빠지지 않을 까 할 수 있지만,

    rx.tex는 직접적인 값 주입에는 반응하지 않고 오로지 유저가 입력하거나 하는 이벤트에만 동작한다.

     

    이 부분은 나중에 다시 다뤄보겠다

    'TroubleShooting' 카테고리의 다른 글

    [TS] CollectionView.rx.itemSelected가 먹통이 된 이유  (0) 2025.02.09
    댓글