🐘

[SwiftUI] Buttonの反応がおかしい時にやること

2023/06/01に公開

Mastodonのアプリを作っていてSwiftUIのボタン周りでいくつかハマったところがあったので紹介します。
https://apps.apple.com/jp/app/nightfox-dawn-for-mastodon/id1668645019?l=en

画像ボタンの見えない部分にタップ判定がある

次のコードは16:9の横長の画像を正方形にクロップしたボタンを作るコードです。

Button {
    
} label: {
    Image()
        .resizable()
        .scaledToFill()
}
.frame(width: 100, height: 100)
.clipped()

これによって作られたボタンは、clippedされている本来画像が表示される領域にタップ判定があります。

これを防ぐためには、label側でframeを指定する必要があります。

Button {
    
} label: {
    Image()
        .resizable()
        .scaledToFill()
        .frame(width: 100, height: 100)
        .clipped()
}

画像ボタンの見えない部分に置かれたUIViewRepresentableのタップ判定が無くなる

前項のようなボタンの周辺に、UIViewRepresentableで作ったビューを配置するとタップが効かなくなります。
下のgifでは、Buttonの上にUIViewRepresentableでラップしたUITextViewにリンクを表示していますが、ボタンの外周は反応せず左端だけが有効な状態になっています。

この問題を回避するには、UIViewRepresentableのzIndexをButtonよりも手前にすることで回避できます。

VStack {
    UIKitView()
      .zIndex(1) // added
    Button {
        
    } label: {
        Image()
            .resizable()
            .scaledToFill()
            .frame(width: 300, height: 200)
            .clipped()
    }
}

UIHostingConfigurationに乗せたButtonがスクロール中に押されてしまう

UIHostingConfigurationに乗せたボタンはViewController側にPanジェスチャが設定されているとキャンセル判定が甘くなります。
シートモーダルなどの中でUIHostingConfigurationを使うとスワイプダウンのジェスチャーと干渉します。

Discussion