【问题标题】:SwiftUI: Custom button does not recognize touch with clear background and buttonStyleSwiftUI:自定义按钮无法识别具有清晰背景和 buttonStyle 的触摸
【发布时间】:2020-04-21 19:59:47
【问题描述】:

我偶然发现了 SwiftUI 中 Buttons 与自定义 ButtonStyle 相结合的奇怪行为。

我的目标是创建一个带有某种“推回动画”的自定义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)
    }
}

当我现在尝试在Text 视图之外触摸此按钮时,什么都不会发生。没有动画可见,也不会调用动作块。

到目前为止我发现了什么:

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

现在的问题是,是否有人对如何正确解决此问题或如何解决问题有更好的了解?

【问题讨论】:

    标签: ios swift swiftui xcode11


    【解决方案1】:

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

    使用 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)
        }
    }
    

    【讨论】:

    • 我浪费了我生命中的一个小时试图解决这个问题。谢谢!
    【解决方案2】:

    只需使用 .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)
        }
    }
    

    我希望我能帮上忙。 :-)

    @用视频编辑。

    【讨论】:

    • 我明白你在想什么。这可能在这个用例中有效,但在更复杂的用例中也存在Spacer(),它不再有效,因为Text 不能具有完整的宽度/高度。
    • 使用 Spacer() 查看我的最小编辑答案。它确实按预期工作。 SwiftUI 中的文本与“.infinity”所允许的位置一样多。当然,你也可以定义一个值。
    • 但我最初的用例是在ButtonStackSpacer inside 中,正如您在我的Playground_Previews 中看到的那样。在那里我认为它不会起作用。
    • 在我的回答中添加了这个场景并制作了一个视频来向你展示它的工作原理。当然,Asperis 的答案也很好。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-12
    • 1970-01-01
    • 1970-01-01
    • 2020-04-08
    • 2022-01-23
    • 1970-01-01
    相关资源
    最近更新 更多