Bill Rei
Bill Rei

Reputation: 453

SwiftUI Can't Change Color Button from Class Method

i've created a KetupatButton class with 2 method like this:

class KetupatButton {
var condition = false
@State var colorToShow = Color(UIColor(red: 183/255, green: 191/255, blue: 150/255, alpha: 100))

func colorChange() {
    if condition == false {
        colorToShow = Color(UIColor(red: 183/255, green: 191/255, blue: 150/255, alpha: 100))
    } else {
        colorToShow = Color(UIColor(red: 19/255, green: 58/255, blue: 27/255, alpha: 100))
    }
}

func createButton(size: CGFloat) -> some View {
    return AnyView(Button(action: {
        self.condition = !self.condition
        self.colorChange()
        print(self.colorToShow)
        print(self.condition)
    }, label: {
        Rectangle()
            .frame(width: size, height: size, alignment: .leading)
            .foregroundColor(colorToShow)
    }))
}

}

But, when I call that class from my ContentView and tap the button, the button don't change it's color. Even though when i print the colorToShow variable, it changed. But the UI color of the button didn't change...

Here is my ContentView

struct ContentView: View {
var button1 = KetupatButton()

var body: some View {
    button1.createButton(size: 200)
}

}

Upvotes: 0

Views: 55

Answers (1)

HunterLion
HunterLion

Reputation: 4006

You should follow a more structured approach: separate your view model, which is your class, from the views. Here are some steps to follow:

  1. Your view model should not contain the view, if you can avoid it. Create a specific view to show the button, separated from the logic. Here's how your button could look like:
struct ButtonView: View {
    
    // Receive the view model
    @ObservedObject var viewModel: KetupatButton
    let size: CGFloat
    
    var body: some View {
        Button {

            // Use this to animate the change
            withAnimation {

                // The condition will automatically change the color, see the view model code
                viewModel.condition.toggle()
            }
        } label: {
            Rectangle()
                .frame(width: size, height: size, alignment: .leading)
                .foregroundColor(viewModel.colorToShow)
        }
    }
}
  1. Your view model should be an Observable Object. Also, the values that trigger a change in the UI should be @Published variables. In addition, I also made in a way that the color changes automatically when the condition changes, so you can get rid of colorChnage().
class KetupatButton: ObservableObject {
    
    // When this variable changes, it will change also the color.
    var condition = false {
        didSet {
            if condition {
                colorToShow = Color(UIColor(red: 19/255, green: 58/255, blue: 27/255, alpha: 100))
            } else {
                colorToShow = Color(UIColor(red: 183/255, green: 191/255, blue: 150/255, alpha: 100))
            }
        }
    }

    @Published var colorToShow = Color(UIColor(red: 183/255, green: 191/255, blue: 150/255, alpha: 100))
}
  1. Finally, ContentView should create an instance of the view model as a @StateObject to share with subviews, like ButtonView that we created above:
struct ContentView: View {
    @StateObject var viewModel = KetupatButton()

    var body: some View {
        ButtonView(viewModel: viewModel, size: 200)
    }
}

Now, you can see that the color of the button changes.

Upvotes: 1

Related Questions