SwiftUIでは@FocusStateを使うことで入力フォームのフォーカスを制御出来ます。
フォーカスを外してソフトウェアキーボードを閉じたり、
他の入力フォームへ移動したり出来ます。
@FocusStateはiOS15からの機能ですので、
その点には気を付けて下さい。
@FocusStateとは
@FocusStateはProperty Wrapperです。
@Stateや@ObservedObjectでお馴染みですね。
同じようにViewで変数宣言する際に指定します。
@FocusState var focus:Bool
初期値は設定できません。
使用できるのはBool又はHashableなものになります。
Hashableの使い方については後述。
入力フォームに対してFocusStateの変数を割り当てます。
focused Modifierを使用します。
TextField("TextField", text: self.$text)
.focused(self.$focus)
これで変数focusにTextFieldのフォーカスが反映される様になりました。
この変数のtrue/falseを切り替える事で、コード側からの変更も可能です。
フォーカスのON/OFFを切り替える
それでは実際にフォーカスを切り替えてみましょう。
FormにTextFieldと2つのButtonを設置し、
Buttonでフォーカス切り替える様にしました。
struct FocusStateBoolView: View {
@State var text = ""
@FocusState var focus:Bool
var body: some View {
Form {
TextField("TextField", text: self.$text)
.focused(self.$focus)
Button("Focus ON"){
self.focus = true
}
Button("Focus OFF"){
self.focus = false
}
}
}
}
TextFieldに触れずともボタンのみでフォーカスを制御できました。
ソフトウェアキーボードを使用している場合はフォーカスを外す事でクローズするので、
キーボードのreturnを押さずに閉じる事が出来ます。
TextEditerなどの改行を受け付ける入力フォームを使う際に有効でしょう。
フォーカスの移動
入力フォームが複数ある場合にも@FocusStateは有効です。
はじめにBool型で説明しましたが、複数ある場合はHashableを使います。
全ての入力フォームにBool型変数をそれぞれ用意する必要はありません。
struct FocusStateMoveView: View {
enum Field:Hashable{
case firstName
case lastName
}
@State var firstName = ""
@State var lastName = ""
@FocusState var focus:Field?
var body: some View {
Form {
TextField("FirstName", text: self.$firstName)
.focused(self.$focus, equals: Field.firstName)
TextField("LastName", text: self.$lastName)
.focused(self.$focus, equals: Field.lastName)
Button("FirstName"){
self.focus = .firstName
}
Button("LastName"){
self.focus = .lastName
}
Button("Close"){
self.focus = nil
}
}
}
}
enumにHashableを継承して使っています。
これはfocused ModifierにHashableな変数が要求されるからです。
変数はOptionalにします。
またfocusedの第二引数equalsにはその入力フォームに対応する値を指定します。
@FocusState var focus:Field?
TextField("FirstName", text: self.$firstName)
.focused(self.$focus, equals: Field.firstName)
コード側から変更する際はequalsに指定した値を変数に代入するとフォーカスが切り替わります。
フォーカスを外す際はnilを代入します。
実用的なフォーカス制御UI
実際にフォーカス制御をする際にはFormにボタンを並べるだけではあまり意味がありません。
やはり置くならばキーボードの側でしょう。なのでtoolbarを使って配置します。
struct FocusStateView: View {
enum Field:Int,Hashable{
case FirstName
case LastName
}
@State var firstName = ""
@State var lastName = ""
@FocusState var focus:Field?
var body: some View {
Form {
TextField("FirstName", text: $firstName)
.focused($focus, equals: Field.FirstName)
TextField("LastName", text: $lastName)
.focused($focus, equals: Field.LastName)
}.toolbar{
ToolbarItem(placement: .keyboard){
HStack{
Button(action: {
focus = Field(rawValue: focus!.rawValue - 1)
}){
Image(systemName: "chevron.up")
}
Button(action: {
focus = Field(rawValue: focus!.rawValue + 1)
}){
Image(systemName: "chevron.down")
}
Spacer()
Button("Close"){
focus = nil
}
}
}
}
}
}
キーボードのツールバーにボタンを置いて、そこからフォーカスの操作を行う様にしました。
これで画面上部を触ることなくキーボード近辺のみで操作できるようになりました。
ToolbarItemに.keyboardを指定できるのはiOS15からですが、
@FocusStateもiOS15からなので特に問題はないでしょう。
なお実用的とは言ったものの、
フォーカス切り替えのためのenumのprev/next操作はサクッと適当に書いたものなので、
必要に応じてより良い形にして使用してください。
※余談ですがシミュレータでソフトウェアキーボードを使用する際は「cmd+shift+K」
又は「I/O→Keyboard→Connect HardwareKeyboard」で切り替えて下さい。
コメント