【SwiftUI】List内に複数のButtonがある場合に動かなくなる対処
動画のようにList内に複数のButtonが設置したい場合は多々あります。
しかしながら、Buttonが複数ある場合、動画の一行目のように意図したようにButtonが動作しないことが生じます。今回はこれを解決して、四行目のような動作になるような対処法について説明していきます。
開発環境
Xcode : 14.3
Swift : 5.8
SwiftUIを使用
再現コード
List {
HStack {
Button {
print("button 1 tapped")
} label: {
Text("One")
}
Button {
print("button 2 tapped")
} label: {
Text("Two")
}
}
}
このコードを実行すると、ボタンのタップが適切に認識されません。なぜなら、SwiftUIのListはその行全体をタップ可能な領域として認識し、ユーザが行自体をタップしたと解釈します。この結果、行内のButtonのタップイベントは上書きされてしまいます。
この問題の根本的な原因はタップイベントの管理が競合してしまう点にあります。Listはその全体が一つのタップ領域となり、Buttonも同様に自身が独立のタップ領域であると認識します。そのため、ユーザがButtonをタップしてもListのタップイベントが優先され、Buttonのタップイベントが無視されます。
この問題を解決するには2つのアプローチがあります。
解決策1:buttonStyleを使う
Buttonには**buttonStyle
修飾子があります。これを使用してButtonに.borderless
または.plain
**のスタイルを適用すると、ボタンのタップが正しく認識されます。
List{
HStack {
Button(action: {
print("button 1 tapped")
}) {
Text("One")
}
.buttonStyle(.borderless)
Button(action: {
print("button 2 tapped")
}) {
Text("Two")
}
.buttonStyle(.plain)
}
}
**.borderless
や.plain
**スタイルを適用すると、Buttonはタップ可能な領域を自身のラベル(この場合はText)に制限します。これにより、Buttonのタップ領域とListのタップ領域が競合することを避け、Buttonのタップイベントが正しく検出されます。
解決策2:onTapGestureを使う
もう1つの解決策はButtonをTextに変え、**onTapGesture
**を使用する方法です。これにより、Textのタップイベントを捉えることができます。
List {
HStack {
Text("One").onTapGesture {
print("One")
}
Text("Two").onTapGesture {
print("Two")
}
}
}
この方法では、Buttonを使用する代わりにTextにタップジェスチャーを付与します。Textはタップ領域が自身に限定されるため、Listのタップ領域と競合しません。
まとめ
List内にBUttonを設置するのはかなり一般的な実装と思われるのですが、このようなバグが発生するのはなかなか辛いところがありますね。。。
いいね、ブックマーク、フォローしていただけると勉強の励みになりますので是非お願いします。😉
追伸 --
Twitterで日々の学習風景を投稿してます。
参考にさせていただいたサイト様
https://stackoverflow.com/questions/56561064/swiftui-multiple-buttons-in-a-list-row!