7
2

[SwiftUI] Buttonの中にButtonを置いたときのハイライト問題について

Last updated at Posted at 2023-12-15

この記事はand factory.inc Advent Calendar 2023の16日目の記事です。

はじめに

SwiftUIでButtonの中に別のButtonを配置したいときがありました。
例えば、Cellのように全体がタップ可能なViewの中にタップ可能なアイコンを配置するようなときです。
この場合、以下のようにアイコンをタップするとCell全体ハイライトが効いてしまい、Cell自体をタップしたかのような状態になってしまいます。

gif1.gif

アイコンをタップしたときは、Cell全体にはハイライトが効かないようにしたかったのですが、容易には実装できなかったので色々と試行錯誤した結果をお伝えしようと思います。

結論からいうと、理想的な解決法は見つけることがまだできていません。もしいい案があればコメントしていただけると幸いです。

試したこと

1. ボタンアイコンをCellの上に配置する

アイコンのButtonがCell全体を覆っているButtonの中にあることで、アイコンをタップしたときにCell全体を覆っているButtonもタップイベントを受け取ってしまいCell全体がハイライトされてしまっていると考えました。
そこで、ZStackを使ってアイコンをCellを覆っているButtonの中ではなく、Z軸上で上に配置することで解決を図りました。

ZStack(alignment: .trailing) {
            Button {
                print("$$$ アイテム2がタップされました")
            } label: {
                HStack(spacing: 50) {
                    VStack(alignment: .leading, spacing: 8) {
                        Text("アイテム2")
                            .foregroundColor(.primary)
                            .font(.headline)
                            .frame(maxWidth: .infinity, alignment: .leading)

                        Text("ボタンの上にボタン")
                            .foregroundColor(.primary)
                            .font(.caption)
                    }


                    Spacer()
                }
                .padding(.horizontal, 24)
                .frame(width: 300, height: 80)
                .background(Color.blue.opacity(0.6))
            }

            Button {
                print("アイテム2のアイコンがタップされました")
            } label: {
                Image(systemName: "trash.fill")
                    .resizable()
                    .tint(.red)
                    .frame(width: 30, height: 30)
            }
            .padding(.trailing, 24)
        }

この実装の場合、アイコンをタップしたときはアイコンのみがハイライトされCell全体がハイライトされなくなりました。
しかし、アイコンがCellを覆っているButtonの上に配置されていることでCell自体をタップしたときにアイコンはハイライトされません。

gif2.gif

2. アイコンのみCellの中に残してButtonをCellの上に配置する

1の場合のCell自体をタップしたときにアイコンがハイライトされないことに対処するために、アイコンのImageとButtonを分割した実装が以下になります。

ZStack(alignment: .trailing) {
            Button {
                print("$$$ アイテム3がタップされました")
            } label: {
                HStack(spacing: 50) {
                    VStack(alignment: .leading, spacing: 8) {
                        Text("アイテム3")
                            .foregroundColor(.primary)
                            .font(.headline)
                            .frame(maxWidth: .infinity, alignment: .leading)

                        Text("ボタンの中にアイコン")
                            .foregroundColor(.primary)
                            .font(.caption)
                    }

                    Image(systemName: "trash.fill")
                        .resizable()
                        .tint(.red)
                        .frame(width: 30, height: 30)
                }
                .padding(.horizontal, 24)
                .frame(width: 300, height: 80)
                .background(Color.blue.opacity(0.6))
            }

            Button {
                print("アイテム3のアイコンがタップされました")
            } label: {
                Rectangle()
                    .foregroundColor(.clear)
                    .frame(width: 40, height: 40)
                    .border(Color.black)
                    .contentShape(Rectangle())

            }
            .padding(.trailing, 19)
        }

アイコンのImage自体はCellを覆っているButtonの中に配置し、アイコンのButtonはZStackでアイコンImageのちょうど真上に来るようにCellを覆っているButtonの上に配置するような形です。

gif3.gif

このようにすることで、Cell自体をタップしたときはアイコンも一緒にハイライトされますが、逆にアイコンをタップしたときにアイコン自体がハイライトされない問題に直面してしまいました。

3. onTapGestureやonLongPressGestureを使った方法

Buttonを使わずにタップイベントをonTapGestureやonLongPressGestureを使って回避する方法も模索しましたが、Buttonをタップしたときの挙動やアニメーションを再現するのが難しく、よい解決方法が見つけれれていません。

まとめ

以上が、SwiftUIでButtonの中にButtonを配置したときのハイライト問題を回避しようと模索した結果になります。
今後も引き続き、良い解決方法を模索していきたいと思いますが、もし良い案があればコメントいただけると幸いです。

最後まで読んでいただきありがとうございました。

明日のAdvent Calenderもお楽しみに!

7
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
2