반응형

SwiftUI Study – FocusState 고급 활용: 입력 폼 흐름 제어·자동 포커싱·키보드 관리 패턴

 

1. 왜 중요한가 (문제 배경)

SwiftUI의 @FocusState는 UIKit의 becomeFirstResponder()를 대체하는 핵심 기능이지만, 실무에서는 다음 문제가 자주 발생한다.

  • 여러 TextField 간 포커스 이동이 불안정
  • 화면 전환 직후 포커스가 올라오지 않음
  • 키보드가 닫혀도 FocusState 값은 true로 남아 UI와 불일치
  • 조건부 렌더링(if) 때문에 포커스가 사라지거나 적용 실패
  • 비동기 로직 이후 포커스 주기 실패

이는 FocusState의 렌더링 타이밍, 바인딩 구조, 입력 흐름 제어 방식을 정확히 이해하면 해결할 수 있다.

 

2. 잘못된 패턴 예시

❌ 예시 1: TextField가 렌더되기 전에 포커스 적용

@FocusState private var focused: Bool

if isVisible {
    TextField("이름", text: $name)
        .focused($focused)
        .onAppear {
            focused = true   // ❌ 렌더 이전 호출 → 포커스 실패
        }
}

문제점

- 조건부 렌더 상황에서 TextField가 생성되기도 전에 포커스 명령 실행

- SwiftUI는 이를 무시 → 포커스 실패 

❌ 예시 2: 여러 TextField가 동일 FocusState 사용

@FocusState private var isFocused: Bool

TextField("A", text: $a).focused($isFocused)
TextField("B", text: $b).focused($isFocused)   // ❌ 어느 필드가 활성인지 모름

문제점

- 어떤 필드를 선택해야 하는지 구분 불가

- 입력 흐름 제어가 어려워짐 

❌ 예시 3: 키보드 내려가도 FocusState는 true

TextField("메모", text: $memo)
    .focused($isFocused)

Button("완료") {
    isFocused = false   // ❌ 상태와 UI 타이밍이 맞지 않을 수 있음
}

문제점

- UI와 상태의 불일치 가능

- 애니메이션/비동기 전환 중 특히 빈번하게 나타나는 문제 

❌ 예시 4: onSubmit 자동 이동 불안정

.onSubmit {
    focusedField = .password   // ❌ 조건부 렌더 + 비동기 업데이트 시 실패
}

 

3. 올바른 패턴 예시

✅ 예시 1: FocusState는 enum 기반이 가장 안전

enum Field: Hashable {
    case name
    case email
    case password
}

@FocusState private var focusedField: Field?

적용:

TextField("이름", text: $name)
    .focused($focusedField, equals: .name)

장점

- 어떤 필드인지 명확

- 입력 흐름 제어가 구조적으로 깔끔 

✅ 예시 2: 렌더링 이후 포커스 안정 적용

.onAppear {
    DispatchQueue.main.async {
        focusedField = .name
    }
}

또는 Swift concurrency:

.task {
    try? await Task.sleep(nanoseconds: 50_000_000)
    focusedField = .name
}

장점

- TextField가 실제 View 계층에 올라온 후 포커스 적용

- sheet·navigation push 환경에서도 안정적 

✅ 예시 3: 자연스러운 자동 이동 구현

TextField("이메일", text: $email)
    .focused($focusedField, equals: .email)
    .submitLabel(.next)
    .onSubmit {
        focusedField = .password
    }

장점

- UIKit 수준의 자연스러운 입력 플로우 구현

- 키보드 Return 키 자동 변경 

✅ 예시 4: 키보드와 FocusState 타이밍 보정

Button("확인") {
    focusedField = nil
    Task {
        try? await Task.sleep(nanoseconds: 10_000_000)
        hideKeyboard()     // UIViewController extension 활용
    }
}

장점

- SwiftUI 상태 변경과 UIKit 키보드 dismiss 타이밍 불일치 해결 

✅ 예시 5: 조건부 렌더링 환경에서 포커스 유지

.onChange(of: step) { newStep in
    if newStep == .email {
        DispatchQueue.main.async {
            focusedField = .email
        }
    }
}

장점

- multi-step 입력 UI에서도 안정적 포커스 흐름 제공 

 

4. 실전 적용 팁

✔ 팁 1

FocusState는 enum 기반이 가장 안정적이다.

✔ 팁 2

포커스는 렌더 후에 적용하는 습관을 들여라

→ DispatchQueue.main.async 또는 Task.sleep 사용.

✔ 팁 3

submitLabel(.next) + onSubmit 조합을 적극 활용하면

입력 흐름이 매우 자연스러워진다.

✔ 팁 4

조건부 렌더링(UI 전환)에는 반드시 delay 기반 포커스 적용을 사용해야 한다.

✔ 팁 5

키보드와 FocusState는 타이밍이 항상 100% 일치하지 않는다.

필요하면 UIKit 키보드 dismiss와 조합해 조정해야 한다.

 

5. 정리

  • @FocusState는 SwiftUI 입력 UI를 구성할 때 필수적인 기술이다.
  • 렌더 타이밍, enum 기반 포커스, 자동 이동 플로우, 키보드 타이밍 조절을 이해하면
    SwiftUI에서도 UIKit 수준의 안정적 입력 UX를 구현할 수 있다.
  • 회원가입, 결제, 주소 입력 같은 복잡한 폼에서 특히 강력한 효과를 발휘한다.
반응형
Posted by 까칠코더
,