SwiftUI:自定义按钮在透明背景和buttonStyle下无法识别触摸

9

我在使用自定义ButtonStyle与SwiftUI中的Button时,遇到了一种奇怪的行为。

我的目标是创建一个带有某种“推回动画”的自定义ButtonStyle。 我使用以下设置:

struct CustomButton<Content: View>: View {
    private let content: () -> Content

    init(content: @escaping () -> Content) {
        self.content = content
    }

    var body: some View {
        VStack {
            Button(action: { ... }) {
                content()
            }
            .buttonStyle(PushBackButtonStyle(pushBackScale: 0.9))
        }
    }
}

private struct PushBackButtonStyle: ButtonStyle {
    let pushBackScale: CGFloat

    func makeBody(configuration: Self.Configuration) -> some View {
        configuration
            .label
            .scaleEffect(configuration.isPressed ? pushBackScale : 1.0)
    }
}

// Preview
struct Playground_Previews: PreviewProvider {
    static var previews: some View {
        CustomButton {
            VStack(spacing: 10) {
                HStack {
                    Text("Button Text").background(Color.orange)
                }

                Divider()

                HStack {
                    Text("Detail Text").background(Color.orange)
                }
            }
        }
        .background(Color.red)
    }
}

现在,当我尝试点击

showcase

我所发现的:

  • 当您删除 .buttonStyle(...) 时,它会按预期工作(当然不包括自定义动画)
  • 或者当您在CustomButton中的VStack上设置.background(Color.red))时,它也会与.buttonStyle(...)结合使用按预期工作

现在的问题是是否有更好的方法来正确解决此问题或修复它?

2个回答

18

只需在自定义按钮样式中添加命中测试内容形状,如下所示

已在Xcode 11.4 / iOS 13.4上进行过测试

演示

private struct PushBackButtonStyle: ButtonStyle {
    let pushBackScale: CGFloat

    func makeBody(configuration: Self.Configuration) -> some View {
        configuration
            .label
            .contentShape(Rectangle())     // << fix !!
            .scaleEffect(configuration.isPressed ? pushBackScale : 1.0)
    }
}

1
我浪费了一小时的时间来尝试修复这个问题。谢谢! - cohen72

0

只需使用 .frame,它就应该可以工作。 为了使其易于测试,我已将其重写如下:

struct CustomButton: View {

    var body: some View {
                    Button(action: {  }) {
        VStack(spacing: 10) {
            HStack {
                Text("Button Text").background(Color.orange)
                .frame(minWidth: 0, maxWidth: .infinity)
                .background(Color.orange)
            }

            Divider()

            HStack {
                Text("Detail Text").background(Color.orange)
                .frame(minWidth: 0, maxWidth: .infinity)
                .background(Color.orange)
            }
                        }
    }
        .buttonStyle(PushBackButtonStyle(pushBackScale: 0.9))
    }
}

private struct PushBackButtonStyle: ButtonStyle {
    let pushBackScale: CGFloat

    func makeBody(configuration: Self.Configuration) -> some View {
        configuration
            .label
            .scaleEffect(configuration.isPressed ? pushBackScale : 1.0)
    }
}

希望我能帮到你。:-)

@编辑,附上视频。

enter image description here


我明白你在想什么。这在这种用例中可能有效,但在更复杂的用例中,如果还有Spacer()存在,它就不再起作用了,因为Text无法具有完整的宽度/高度。 - Stone
看一下我最小化编辑过的答案,里面包含一个Spacer()。它确实按照预期工作。在SwiftUI中,文本所占的空间取决于我们允许它使用的".infinity"的量。当然,你也可以定义一个值。 - Kuhlemann
但是我的初始用例是在 Button 内部使用 StackSpacer,就像您可以在我的 Playground_Previews 中看到的那样。在那里,我认为它将无法工作。 - Stone
我在我的答案中添加了这种情况,并制作了一个视频来展示它是如何工作的。当然,Asperis的答案也很好。 - Kuhlemann

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接