Home > database >  SwiftUI: Button inside function doesn't update view
SwiftUI: Button inside function doesn't update view

Time:03-15

In my view, I'm trying to implement a button that is returned by a function buttonView(). The button displays whether a bool value is true, false, or nil. The button starts off as nil, and switches between true and false on each click of the button, updating the view to show the latest value of the bool.

The issue: The Text showing the latest value of the bool does not change/update on click.

What's interesting is that the same button works/updates perfectly fine when it isn't placed in a function but rather in the view itself.

The following SwiftUI code shows the code I'm working with. (I'm in XCode 13.2.1 on macOS Monterey)

struct TestView: View {
    
    var body: some View {
        buttonView()          
    }
    
    func buttonView() -> some View {
        @State var testBool: Bool?        // THE BOOL CHANGING/BEING SHOWN
        
        return Button(action: {
            testBool = Bool.random()      // SETS BOOL VALUE ON CLICK
        }, label: {
            VStack {
                Text("click me")
                Text(testBool == nil ? "nil" : String(testBool!))  //DISPLAYS BOOL VALUE
            }
        })
    }
}

CodePudding user response:

You have @State defined inside a member function. Instead, it should be defined at the top level of your View -- that's the only way it will retain/update state. The smallest working change would be this:

struct TestView: View {
    @State var testBool: Bool? //<-- Here
    
    var body: some View {
        buttonView()
    }
    
    func buttonView() -> some View {
        Button(action: {
            testBool = Bool.random()
        }, label: {
            VStack {
                Text("click me")
                Text(testBool == nil ? "nil" : String(testBool!)) 
            }
        })
    }
}

A further refactor to get it to look a little Swift-ier might be:

struct TestView: View {
    @State var testBool: Bool?
    
    var body: some View {
        Button(action: {
            testBool = Bool.random()
        }) {
            VStack {
                Text("click me")
                if let testBool = testBool {
                    Text(testBool ? "true" : "false")
                } else {
                    Text("nil")
                }
            }
        }
    }
}
  • Related