anz blog

アクセシビリティで「ボタンの形」が有効になっていると隠しリンクが可視化される

2024-10-28 #SwiftUI #Swift

アクセシビリティの設定である項目が有効になっていると予期せぬ表示になってバグり散らかした話。

詳細

SwiftUI でコードで画面遷移させる場合 NavigationLink で隠しリンクを埋め込んでおいて、それをコードで発火させることで画面遷移を実現させるということをるかと思います。

例を雑にかくととこんな感じ

struct SandboxView: View {
    @State var isActive: Bool = false
    var body: some View {
        NavigationView {
            VStack {
                NavigationLink(
                    isActive: $isActive,
                    destination: {
                        Text("Child View")
                    },
                    label: {
                        EmptyView()
                    }
                )
                Spacer()
                Button("tap me") {
                    isActive = true
                }
                Spacer()
            }
        }
    }
}

これでボタンをタップしたときに "Child View" って書かれた画面に遷移できるはず。

が、これデバイスのアクセシビリティ設定でとある項目を有効にしていると、普段見えないはずのこの NavigationLink が可視化されてしまいます。 それが... 「ボタンの形」 です。
これが有効になっていると、ボタン領域をシステム側で強調させて表示させるようになります(フラットデザインとかだとどれがタップできるものかわからない場合とかに有効なのかな)。

この強調して表示した結果 EmptyView() で普段は見えないはずの領域もグレーで表示されてしまいユーザーが普通に見える状態になります。
見えるということはもちろんタップもできます。
本来であれば、 isActive が true になったときに発火するものが、本来意図していないタイミングでも発火させることができるようになり、結果として意図していない挙動になったり作りによってはクラッシュしたりします。

対応

対応方法はいくつかあるっぽいのですが、僕自身は NavigationLink に対して buttonStyle を設定してあげるというもの。

struct SandboxView: View {
    @State var isActive: Bool = false
    var body: some View {
        NavigationView {
            VStack {
                NavigationLink(
                    isActive: $isActive,
                    destination: {
                        Text("Child View")
                    },
                    label: {
                        EmptyView()
                    }
                )
                .buttonStyle(.plain)    // <- 追加
                Spacer()
                Button("tap me") {
                    isActive = true
                }
                Spacer()
            }
        }
    }
}

これをしておくことでアクセシビリティ「ボタンの形」が有効になっていても、隠しリンクが可視化されることはなくなります。
まぁ、EmptyView と指定しているんだから、システム側でここらもよしなにやってほしいというのが本音ではありますが。。

参考