如何在SwiftUI中按下按钮时更改按钮图像

7
当按下按钮时,我想更改图像名称。 目前我的结构如下:
struct ContentView: View { 
    @State var matrix = [Int] var 
    body: some View { 
        VStack { 
            Text("Test") 
            HStack(alignment: .center, spacing: 8) { 
                Button(action: { 
                    **
                    // call a func that will do some check and 
                    // change the image from Blue.png to Red.png
                    ** 
                }) { 
                    Image("Blue") 
                        .renderingMode(.original)
                        .resizable()                               
                        .aspectRatio(contentMode: .fill)
                        .clipShape(Circle()) 
                } 
            } 
        } 
    } 
 } 

https://dev59.com/sFMI5IYBdhLWcg3wUZyd - Shamas S
这个回答解决了你的问题吗?如何设置SwiftUI按钮的自定义高亮状态 - Christophe Douy
1个回答

7
我已经寻找了许多方法解决这个问题,但它们都有缺点。其中一种方法我看到得非常多(这里)是通过创建自定义的ViewModifier来实现,然后在按钮内容上使用DragGesture来识别用户的点击和释放操作。这种解决方案存在许多问题,主要是手势支配了任何其他手势,这使得无法在ScrollView中使用按钮。
从iOS13.0+开始,苹果提供了一个ButtonStyle协议,用于快速设置按钮样式。该协议公开了一个isPressed变量,完美地反映了按钮状态。这意味着当您按住并拖动指头到视图外部时,变量会从true更改为false,这正如您所期望的那样。而之前我链接的解决方案则不具备这个功能。
解决该问题的方法是首先定义一个带有绑定变量isPressed的新ButtonStyle
struct IsPressedRegisterStyle : ButtonStyle {
    @Binding var isPressed : Bool
    func makeBody(configuration: Self.Configuration) -> some View {
        configuration.label
            .onChange(of: configuration.isPressed, perform: {newVal in
                isPressed = newVal
            })
    }
}

然后使用这个样式:

struct IconButton : View {
    @State var width: CGFloat? = nil
    @State var height: CGFloat? = nil
    let action : () -> Void
    
    @State private var isPressed : Bool = false
    
    var body : some View {
        Image(isPressed ? "someImage" : "someOtherImage")
            .renderingMode(.original)
            .resizable()
            .scaledToFit()
            .frame(width: width, height: height)
            .overlay{
                GeometryReader{proxy in
                    Button(
                        action:{
                            action()
                        },
                        label: {
                            Spacer()
                                .frame(width: proxy.size.width, height: proxy.size.height)
                                .contentShape(Rectangle())
                        })
                        .buttonStyle(IsPressedRegisterStyle(isPressed: $isPressed))
                }
            }
    }
}

请注意在定义的底部使用了自定义的ButtonStyle
这里快速介绍一下具体的步骤:
  • 我们创建一个Image图像,并使用spacer将一个Button覆盖在其整个区域上。
  • 点击图像会触发覆盖的按钮。
  • 我们应用的自定义ButtonStyle将按钮的内部变量isPressed暴露给我们的IconButton视图。
  • 我们根据暴露的值设置按钮图像。
你可以像这样实例化一个按钮:
// arbitrary values for width and height
IconButton(width: 200, height: 70){
    print("Button Pressed")
}

我没有对这段代码进行过多的测试,请告诉我是否存在使用上的缺陷。

谢谢!


1
欢迎来到Stack Overflow社区,@Hkon。这是一个非常出色的第一篇回答,提供了我们喜欢看到的详细程度和解释。继续保持好工作。 - Jeremy Caney

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