Color.clear 背景上的 SwiftUI onTapGesture 与 Color.blue 的行为不同

krj*_*rjw 12 swiftui

我想提出一个自定义PickerSegmentedPickerStyle()。我想有相同的行为,但是当我点击内容和其中一个可能选择的边框之间的区域时,onTapGesture它不起作用。当我添加蓝色背景时,它确实有效,但背景清晰时则无效。

使用蓝色背景

使用蓝色背景

无法在清晰的背景下工作

无法在清晰的背景下工作

不工作的代码:

import SwiftUI

struct PickerElementView<Content>: View where Content : View {
    @Binding var selectedElement: Int
    let content: () -> Content

    @inlinable init(_ selectedElement: Binding<Int>, @ViewBuilder content: @escaping () -> Content) {
        self._selectedElement = selectedElement
        self.content = content
    }

    var body: some View {
        GeometryReader { proxy in
            self.content()
                .fixedSize(horizontal: true, vertical: true)
                .frame(minWidth: proxy.size.width, minHeight: proxy.size.height)
                // ##################################################################
                // CHANGE COLOR HERE TO BLUE TO MAKE IT WORK
                // ##################################################################
                .background(Color.clear)
                // ##################################################################
                .border(Color.yellow, width: 5)

        }
    }

}

struct PickerView: View {
    @Environment (\.colorScheme) var colorScheme: ColorScheme

    var elements: [(id: Int, view: AnyView)]

    @Binding var selectedElement: Int
    @State var internalSelectedElement: Int = 0

    private var width: CGFloat = 220
    private var height: CGFloat = 100
    private var cornerRadius: CGFloat = 20
    private var factor: CGFloat = 0.95
    private var color = Color(UIColor.systemGray)
    private var selectedColor = Color(UIColor.systemGray2)


    init(_ selectedElement: Binding<Int>) {
        self._selectedElement = selectedElement
        self.elements = [
            (id: 0, view: AnyView(PickerElementView(selectedElement) {
                    Text("9").font(.system(.title))
            })),
            (id: 1, view: AnyView(PickerElementView(selectedElement) {
                    Text("5").font(.system(.title))

            })),
        ]
        self.internalSelectedElement = selectedElement.wrappedValue
    }

    func calcXPosition() -> CGFloat {
        var pos = CGFloat(-self.width * self.factor / 4)
        pos += CGFloat(self.internalSelectedElement) * self.width * self.factor / 2
        return pos
    }

    var body: some View {
        ZStack {
            Rectangle()
                .foregroundColor(self.selectedColor)
                .cornerRadius(self.cornerRadius * self.factor)
                .frame(width: self.width * self.factor / CGFloat(self.elements.count), height: self.height - self.width * (1 - self.factor))
                .offset(x: calcXPosition())
                .animation(.easeInOut(duration: 0.2))

            HStack {
                ForEach(self.elements, id: \.id) { item in
                    item.view
                        .gesture(TapGesture().onEnded { _ in
                            print(item.id)
                            self.selectedElement = item.id
                            withAnimation {
                                self.internalSelectedElement = item.id
                            }
                        })
                }
            }
        }
        .frame(width: self.width, height: self.height)
        .background(self.color)
        .cornerRadius(self.cornerRadius)
        .padding()
    }
}

struct PickerView_Previews: PreviewProvider {
    static var previews: some View {
        PickerView(.constant(1))
    }
}


Run Code Online (Sandbox Code Playgroud)

更改我标记的颜色。

有谁知道为什么他们的行为不同以及我如何解决这个问题?

E.C*_*oms 19

一行答案不是设置 backgroundColor,而是设置contentShape为命中测试。

 var body: some View {
    GeometryReader { proxy in
        self.content()
            .fixedSize(horizontal: true, vertical: true)
            .frame(minWidth: proxy.size.width, minHeight: proxy.size.height)
            // ##################################################################
            // CHANGE COLOR HERE TO BLUE TO MAKE IT WORK
            // ##################################################################
            .contentShape(Rectangle())
            // ##################################################################
            .border(Color.yellow, width: 5)

    }
}
Run Code Online (Sandbox Code Playgroud)


Mat*_*tič 8

默认情况下,SwiftUI 中的透明视图不可点击,因为它们的内容形状为零。

您可以使用.contentShape修饰符更改此行为:

Color.clear
  .frame(width: 300, height: 300)
  .contentShape(Rectangle())
  .onTapGesture { print("tapped") } 
Run Code Online (Sandbox Code Playgroud)

  • @pleshis 没有 contentShape 就无法工作,无论框架如何。它不会记录对非渲染区域的触摸。这是一个经常非常令人困惑的优化事情。最近在游戏库中遇到了这个问题,尽管您经常需要在其中使用触发器。 (3认同)
  • `.frame(maxWidth: .infinity, maxHeight: .infinity)` 更好 (2认同)

Joh*_* M. 5

Color任何不透明度为 0 的物体都是不可利用的,这似乎是一个设计决定。

Color.clear.onTapGesture { print("tapped") }                 // will not print
Color.blue.opacity(0).onTapGesture { print("tapped") }       // will not print
Color.blue.onTapGesture { print("tapped") }                  // will print
Color.blue.opacity(0.0001).onTapGesture { print("tapped") }  // will print
Run Code Online (Sandbox Code Playgroud)

您可以使用第四个选项来解决这个问题,因为它在视觉上与第一个选项没有区别。

  • 我同意!我将提交反馈并尝试提出更优雅的解决方案。也许其他人可能会回答这个问题或给我正确方向的提示。 (3认同)
  • 是的,可能值得提交反馈,如果能够在透明视图上注册手势会很好。如果默认情况下禁用透明视图的命中测试,那很好,但添加显式手势应该覆盖它,IMO。 (2认同)