SwiftUI: sheetだとiOSの仮想キーボードに「閉じる」が付かない件
iOSでテキストフィールドにフォーカスが当たったとき、仮想?スクリーン?キーボードが表示されます。これには「閉じる」ボタンがついてないので、もし閉じる必要がある場合、自分で閉じる処理を書くか、「閉じる」ボタンを追加してやる必要があります。
「閉じる」ボタンを追加するのはおよそ以下のような感じになります。
自分のビューのテキストフィールドにフォーカスが当たっている場合のみ、キーボードの上に閉じるボタンを表示します。
(「(自分のビューのテキストフィールドに)フォーカスが当たっている場合のみ」の判定を入れなくてもこのケースではうまく行きます。ですが、色々と部品化していくと、他のカスタムビューのテキストフィールドでも同じ処理を書いていて、複数のカスタムビューを組み合わせた際に「閉じる」ボタンがいくつも表示されてしまうという現象が発生します。これはそれを避けるための判定になります)
struct NameView: View {
@State var name: String = ""
@FocusState private var isFocused: Bool
var body: some View {
VStack {
TextField("名前", text: $name)
.focused($isFocused)
.toolbar {
if isFocused {
ToolbarItemGroup(placement: .keyboard) {
Spacer()
Button("閉じる") {
self.isFocused = false
}
}
}
}
}
}
}
ですが、これが効くのはNavigationView
/NavigationStack
の中にある場合のみのようです。
.sheet
から上記のNameView
を表示した場合、これだけだとキーボードの上に閉じるボタンが付きません。
なので、もし.sheet
で表示する場合は、
.sheet(isPresented: $isSheetVisible) {
NameView()
}
ではダメで、
.sheet(isPresented: $isSheetVisible) {
NavigationStack {
NameView()
}
}
のようにNavigationView
かNavigationStack
で囲んでやる必要があります。
(NameView
が他のNavigationStack
からも遷移してくるケースも考慮する場合の話。そうでないなら、NameView
のbody
直下にNavigationStack
を入れてやればよいと思います)
「閉じる」がつかなくなるというか、「閉じる」がつくはずの画面なのについてなくて、他の画面とかを色々触っているうちにいつの間にか「閉じる」がつくようになっている、というケースがあります。
なにか条件があるような気もするのですが、なんなんでしょうか。
ですが、これが効くのは
NavigationView
/NavigationStack
の中にある場合のみのようです。
というわけではないようです。
NavigationView
/NavigationStack
の中でなくとも効くようです。
ということは?
- シートでない場合は、
NavigationView
/NavigationStack
の中にあるかどうかに依らず効く。 - シートの場合は、
NavigationView
/NavigationStack
の中にないと効かない。
ということですかね。
ということは?
- シートでない場合は、
NavigationView
/NavigationStack
の中にあるかどうかに依らず効く。- シートの場合は、
NavigationView
/NavigationStack
の中にないと効かない。
ということですかね。
Appleの.toolbar
のドキュメントにはNavigationStack
の文字はないので、こうではないように見えます。
色々試した結果、NavigationStack
があると最初から表示されるけども、NavigationStack
がないと最初からは表示されず、アプリを中断して再度戻ってきたときなど、何かをきっかけにして表示されるようになるケースがある、ということが分かりました。
なんとなく、スクリーンキーボード(のツールバー)の初期化と、ビュー本体の初期化が別々に行われているのに、.toolbar
はビューの宣言に書かれているので、うまく同期できてないんじゃないかという気がします。
NavigationStack
を入れると何かもうちょっと複雑なことが行われてどういう訳か同期する。
この.toolbar { ToolbarItem(placement: .keyboard) }
はまだ癖がありそうです。
以下のscrapに、.toolbar
を使わないでやるという方法を記載しました。