Santiago Alvarez
Santiago Alvarez

Reputation: 306

Modify state in nested object

im new to Swift and i am having this issue. I want to make a play/pause button in the toolbar and i decided to move the toolbar code to its own object Toolbar. The button should change its image when pressed but when i press it the state doesn't change. What am i doing wrong?

struct ContentView: View {
    var body: some View {
        NavigationView {
            List{
                Text("asdf")
            }
            .toolbar {
                Toolbar()
            }
        }
    }
}

struct Toolbar: ToolbarContent {
    @State var started = false
    
    var body: some ToolbarContent {
        ToolbarItem(id:"start-button", placement: .primaryAction) {
            Button(action: {
                self.started.toggle()
            }) {
                Image(systemName: self.started == true ? "pause.fill" : "play.fill")
                    .foregroundColor(.white)
            }
        }
    }
}

Upvotes: 2

Views: 325

Answers (2)

Raja Kishan
Raja Kishan

Reputation: 18904

Use two-way binding.

struct ContentView: View {
    @State private var started = false //<-- Here
    
    var body: some View {
        NavigationView {
            List{
                Text("asdf")
            }
            .toolbar {
                Toolbar(started: $started) //<-- Here
            }
        }
    }
}

struct Toolbar: ToolbarContent {
    
    @Binding var started: Bool //<-- Here
    
    var body: some ToolbarContent {
        ToolbarItem(id:"start-button", placement: .primaryAction) {
            Button(action: {
                self.started.toggle()
            }) {
                Image(systemName: self.started ? "pause.fill" : "play.fill")
                    .foregroundColor(.white)
            }
        }
    }
}

Upvotes: 3

aheze
aheze

Reputation: 30228

@State only works inside Views, and ToolbarContent isn't a View.

You should keep the @State started inside ContentView, and pass in its wrapped value to the toolbar. Then, use a closure to update it.

struct ContentView: View {
    @State var started = false
    
    var body: some View {
        NavigationView {
            List{
                Text("asdf")
            }
            .toolbar {
                Toolbar(started: started) {
                    started.toggle() /// executed when `pressed` is called
                }
            }
        }
    }
}

struct Toolbar: ToolbarContent {

    var started: Bool
    var pressed: (() -> Void) /// closure here!
    
    var body: some ToolbarContent {
        ToolbarItem(id:"start-button", placement: .primaryAction) {
            Button(action: {
                pressed() /// call the closure
            }) {
                Image(systemName: self.started == true ? "pause.fill" : "play.fill")
                    .foregroundColor(.white)
            }
        }
    }
}

Upvotes: 2

Related Questions