概要
認証コードを受け付けるような画面をSwiftUIで実装しようと思います。
1文字入力したら、次のフィールドへフォーカスしたり、backspace押下でフォーカスを一つ戻します。
また、アーキテクチャにはMVVMを使用しています。
環境
Xcode 14.2
実装
まず、UIViewRepresentable
を使ってUITextField
をSwiftUIで扱えるようにします。
updateUIView
を使って、テキストフィールドのフォーカスを制御します。
ざっくり以下の手順を想定してます。
-
UITextField
をSwiftUIで扱えるようにする - ViewModelでフォーカス中のフィールド情報を更新する
UIViewRepresentable
注目すべきところは、引数でtag
, focusTag
を受け取っていることです。
focusTagは現在画面でフォーカスが当たっているフィールドのタグです。
tag
とfocusTag
の値が等しい時にフォーカスを当てます。
struct AuthCodeTextFieldRepresentable: UIViewRepresentable {
...
@Binding var focusTag: Int?
...
private let tag: Int
...
init(
...
focusTag: Binding<Int?>,
...
tag: Int,
...
) {
self._focusTag = focusTag
...
self.tag = tag
...
}
...
func updateUIView(_ uiView: BaseUITextField, context: Context) {
if uiView.tag == focusTag {
uiView.becomeFirstResponder()
}
}
...
}
ViewModel
現在フォーカスしているフィールドの情報はViewModelで管理します。
最初はどこにもフォーカスさせないので、nilで初期化しています。
UITextField
の入力イベントはPassthrouSubject
を使うことで受け取っています。
class HomeViewModel: ObservableObject {
...
@Published var focusTag: Int? = nil
...
...
private func subscribeEditingChanged() {
editingChangedSubject
.receive(on: DispatchQueue.main)
.sink { [weak self] _ in
guard let self = self else { return }
if self.focusTag == .zero {
self.focusTag = 1
} else if self.focusTag == 1 {
self.focusTag = 2
} else if self.focusTag == 2 {
self.focusTag = 3
} else if self.focusTag == 3 {
self.focusTag = 4
} else if self.focusTag == 4 {
self.focusTag = 5
}
}
.store(in: &cancellables)
}
...
}
ソースコードをこちらに添付します。