Search code examples
swiftxcodeswiftuixcode12macos-big-sur

How do I change a button in SwiftUI after a button is pressed


I want a card image to change to another card image when I press a button. This is my current code:

import SwiftUI

var leftCard = "green_back"
var rightCard = "blue_back"

func dealCards() {
    leftCard = "1C"
    print("deal")
}

struct GameView: View {
    var body: some View {
        VStack {
            HStack(alignment: .center, spacing: 20) {
                Image(leftCard)
                    .resizable()
                    .aspectRatio(contentMode: .fit)
                    
                Image(rightCard)
                    .resizable()
                    .aspectRatio(contentMode: .fit)
            }
            .padding(.all, 25)
            
            Button(action: dealCards) {
                Text("Deal Cards")
                    .fontWeight(.bold)
                    .font(.title)
                    .padding(10)
                    .background(Color.blue)
                    .foregroundColor(.white)
                    .padding(10)
                    .border(Color.blue, width: 5)
            }
        }
    }
}

struct GameView_Previews: PreviewProvider {
    static var previews: some View {
        GameView()
    }
}

This code prints "deal" when I press the button, but it doesn't change the image.

I am using MacOS Big Sur beta 3 and Xcode 12 beta 2.

Edit: I just had to move my variables into the GameView struct and add a @State modifier to them. Thanks to everyone who answered. :D


Solution

  • The following code should update your Images and should give you a random value as well in the deal() function.

    It is very important that you define your properties and functions inside a View or component. Global variables are generally not recommended and can lead to unexpected results.

    import SwiftUI
    
    struct GameView: View {
    
        // 1. Add ImageName to handle names via dot (.) to avoid mistyping strings. CaseIterable allow you to get an array of all the values defined.
        enum ImageName: String, CaseIterable {
            case greenBack = "green_back"
            case blueBack = "blue_back"
            case other = "1C"
        }
    
        // 2. Add @State to update the GameView() automatically when any of the properties are updated.
        @State private var leftCard: ImageName = .greenBack
        @State private var rightCard: ImageName = .blueBack
    
        var body: some View {
            VStack {
                HStack(alignment: .center, spacing: 20) {
    
                    // 3. Add .rawValue to get the raw String value
                    Image(leftCard.rawValue)
                        .resizable()
                        .aspectRatio(contentMode: .fit)
    
                    Image(rightCard.rawValue)
                        .resizable()
                        .aspectRatio(contentMode: .fit)
                }
                .padding(.all, 25)
    
                Button(action: dealCards) {
                    Text("Deal Cards")
                        .fontWeight(.bold)
                        .font(.title)
                        .padding(10)
                        .background(Color.blue)
                        .foregroundColor(.white)
                        .padding(10)
                        .border(Color.blue, width: 5)
                }
            }
        }
    
        func dealCards() {
            // 4. Update the name via dot(.) of any card that you want.
            // You can also randomise the card doing .allCases.randomElement()
            leftCard = ImageName.allCases.randomElement() ?? .greenBack
            print("deal")
        }
    }
    
    struct GameView_Previews: PreviewProvider {
        static var previews: some View {
            GameView()
        }
    }