SwiftUIで List
の同じ行に複数の Button
を置くと、すべてのButtonのアクションイベントが発生してしまい、それぞれのボタンを個別にタップできない。
本記事では、個別にタップする方法と、よりボタンらしく見せるために ButtonSyle を利用する方法を紹介する。
実行環境
- Xcode 11.6
- iOS 13.6
問題の挙動
SwiftUIで List
の同じ行に複数の Button
を置くと、すべてのButtonのアクションイベントが発生してしまい、それぞれのボタンを個別にタップできない。
UIKitでのUITableViewのような画面をSwiftUIで作る場合には List
を使う。
import SwiftUI struct SampleButtonOnListView : View { var body: some View { List { Section { Button(action: { print("button 1") }, label: { Text("button 1") }) HStack { Button(action: { print("button 3-1") }, label: { Text("button 3-1") }) Button(action: { print("button 3-2") }, label: { Text("button 3-2") }) } } } } }
実行すると下図のようなUIで表示される。下図では2行目の「button 3-2」のボタンを押しているが、「button 3-1」と「button 3-2」のアクションが両方とも発火している。
ログには下記のように出力されており、それぞれのButtonのアクションを実行することができない。
button 3-1 button 3-2
List の背景をアクティブにせず、ボタンを個別にポチポチする方法はあるのか?
解決編
簡単な方法としてはButtonを使わないようにする。Text
に対してonTapGesture
を使うことで、見た目そのままで個別にタップできるようになる。
struct SampleButtonOnListView : View { var body: some View { List { Section { Button(action: { print("button 1") }, label: { Text("button 1") }) HStack { Text("button 3-1") .onTapGesture { print("button 3-1") } Text("button 3-2") .onTapGesture { print("button 3-2") } } } } } }
タッチイベントを Text
に取られるので、行が反応して背景がハイライト色になることもなくなる。
しかしこれはあくまでも Text
なのでボタンを押した時にハイライト色に変化したりはしない。これでは List の上に Button を置いているとはとても言えない。
List上にButtonを置いて、押されている時にはきちんとハイライト色に変える
やや冗長ではあるが、Button
に対してonTapGesture
Modifierを設定して、ボタンスタイルを適用する。
Button(action: { print("button 3-1") }, label: { Text("button 3-1") }) .onTapGesture { } .buttonStyle(MyButtonStyle())
全体としては下記のように変更した。
import SwiftUI struct SampleButtonOnListView : View { var body: some View { List { Section { Button(action: { print("button 1") }, label: { Text("button 1") }) HStack { Button(action: { print("button 3-1") }, label: { Text("button 3-1") }) .onTapGesture { } .buttonStyle(MyButtonStyle()) Button(action: { print("button 3-2") }, label: { Text("button 3-2") }) .onTapGesture { } .buttonStyle(MyButtonStyle()) } } } } }
実行すると下図のように青い角丸ボタンが表示される。「button 3-1」を押している間、背景の青色がやや半透明になる。
MyButtonStyleでは以下のように指定している。configuration.isPressed
で押しているかどうかを取得することができるので適切な色に変えればよい。
struct MyButtonStyle: ButtonStyle { func makeBody(configuration: Self.Configuration) -> some View { configuration.label .font(.system(size: 17)) .padding() .background(configuration.isPressed ? Color.blue.opacity(0.5) : Color.blue) .foregroundColor(configuration.isPressed ? Color.white.opacity(0.5) : Color.white) .cornerRadius(16.0) } }
ButtonStyleを使う方法についてはまた後日紹介したい。