kentaro

kentaro

[SwiftUI]Gestureの活性・非活性をコントロールするサンプルのiOS14バグ修正

以前allowsHitTesting を利用して Gesture の活性・非活性をコントロールするという記事を書いたのだが、iOS14 で動作しなくなっていたので修正した。

元記事の内容

タイトルのとおり allowsHitTesting() を利用して Gesture の活性・非活性をコントロールするというもの。
この記事の場合 VStack 内にある Toggle は常に活性とさせておきたいので、 VStack 自体にdisabled(_:) をセットするとToggle まで影響を受けてしまい都合が悪い。
基本的には disabled(_:) の利用で問題ないケースが多いと思う。

// iOS13
VStack {
    Toggle(
        "allowsHitTestingのON / OFF",
        isOn: $allowsHitTesting
    )

    Divider()

    Text("allowsHitTestingがONのときは、ダブルタップで背景色が変化します。")
        .frame(
            maxWidth: .infinity,
            maxHeight: .infinity
        )
}
.contentShape(Rectangle())
.padding()
.foregroundColor(
    Color(isDark ? .systemBackground : .label)
)
// ここでGestureの活性・非活性をコントロールしていた
.allowsHitTesting(allowsHitTesting)
.gesture(
    TapGesture(count: 2)
        .onEnded {
            self.isDark.toggle()
        }
)
        .background(
    Color(isDark ? .label : .systemBackground)
        .edgesIgnoringSafeArea(.all)
)

allowsHitTesting を利用する方法だと iOS14 で動作しない

iOS13 では上記で動作していたのだが、iOS14 から Toggle まで allowsHitTesting の影響を受けるようになってしまい、期待する動作をしなくなった。

Configures whether this view participates in hit test operations.

↑ とドキュメントにあるので、そもそも iOS14 での動作のほうが自然な気がする。

対応

Tap を無視したい場合は、 onEnded で何もしない TapGesture を返すようにした。

もうちょい一般的な表現だと、disable な場合に onEnded 等で何もしないクロージャーを返すようにする、かな。

// iOS14

// contentはiOS13の例に記載しているVStackを返す
content
    .contentShape(Rectangle())
    .padding()
    .foregroundColor(
        Color(isDark ? .systemBackground : .label)
    )
    .gesture(
        allowsDoubleTap
            ? TapGesture(count: 2).onEnded { isDark.toggle() }
            // Tapを無視したい場合は、onEndedで何もしないTapGestureを返す
            : TapGesture().onEnded({})
    )
    .background(
        Color(isDark ? .label : .systemBackground)
            .edgesIgnoringSafeArea(.all)
    )

結果

動くようになった 🎉

※動作している様子はリポジトリの README参照

GitHub