Home > Software engineering >  Best way to trigger a button events using @State and @Binding
Best way to trigger a button events using @State and @Binding

Time:07-28

Essentially I would like to trigger the MainButton in ContentView to Open SheetView and then use NavCloseButton to Close the SheetView to Return Back to ContentView. I've been trying to do this using @State and @Binding. While getting SheetView presented using .sheet(isPresented: is simple I'm having trouble dismissing it when the buttons are extracted out.

Can someone please show example how these actions would be performed?

ContentView:

struct ContentView: View {
    
    var body: some View {
        
        NavigationView {
            VStack {
                Spacer()
                Image(systemName: "hand.thumbsup.circle.fill")
                    .resizable()
                    .frame(width: 200, height: 200)
                    .symbolRenderingMode(.hierarchical)
                Spacer()
                MainButton(color: .blue, title: "Tap to Open", image: "lock.open.laptopcomputer")
            }
            .navigationTitle("Page One")
        }
        
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
            .preferredColorScheme(.dark)
    }
}

SheetView:

struct SheetView: View {
    var body: some View {
        
        NavigationView {
            
            VStack {
                Text("Hello, World!")
            }
            .navigationTitle("Sheet View")
            .navigationBarItems(trailing: NavCloseButton(color: .red,
                                                     title: "Close",
                                                     image: ""))
        }
        
    }
    
}

struct SheetView_Previews: PreviewProvider {
    static var previews: some View {
        SheetView()
            .preferredColorScheme(.dark)
    }
}

AppButtons:

struct NavCloseButton: View {
    var color: Color
    var title: String
    var image: String
    
    var body: some View {
        Button {
            print("Closing")
        } label: {
            Text(title)
            .padding()
            .frame(width: 100, height: 40)
            .background(color)
            .foregroundColor(.white)
            .cornerRadius(10)
            .font(.system(.body))
        }
    }
}

struct MainButton: View {
    
    
    var color: Color
    var title: String
    var image: String
    
    
    var body: some View {
        
        Button {
            print("Opened")
        } label: {
            Label {
                Text(title)
            } icon: {
                Image(systemName: image)
            }
            .padding()
            .frame(width: 350, height: 60)
            .background(color)
            .foregroundColor(.white)
            .cornerRadius(15)
            .font(.title2)
        }
    }
}

CodePudding user response:

Following your code this is how you would do it.
Changes are commented.

struct ContentView: View {
    
    @State private var showSheet = false // define state
    
    var body: some View {
        NavigationView {
            VStack {
                Spacer()
                Image(systemName: "hand.thumbsup.circle.fill")
                    .resizable()
                    .frame(width: 200, height: 200)
                    .symbolRenderingMode(.hierarchical)
                Spacer()
                MainButton(color: .blue, title: "Tap to Open", image: "lock.open.laptopcomputer", showSheet: $showSheet) // pass binding
            }
            .navigationTitle("Page One")
            
            .sheet(isPresented: $showSheet) { // present sheet
                SheetView(showSheet: $showSheet) // pass binding
            }
        }
    }
}


struct SheetView: View {
    
    @Binding var showSheet: Bool   // pass binding here

    var body: some View {
        NavigationView {
            VStack {
                Text("Hello, World!")
            }
            .navigationTitle("Sheet View")
            .navigationBarItems(
                trailing:
                    NavCloseButton(color: .red,
                                   title: "Close",
                                   image: "",
                                   showSheet: $showSheet)) // pass binding
        }
        
    }
    
}


struct NavCloseButton: View {
    
    var color: Color
    var title: String
    var image: String
    
    @Binding var showSheet: Bool   // pass binding here

    var body: some View {
        Button {
            print("Closing")
            showSheet = false // set binding to dismiss sheet
        } label: {
            Text(title)
            .padding()
            .frame(width: 100, height: 40)
            .background(color)
            .foregroundColor(.white)
            .cornerRadius(10)
            .font(.system(.body))
        }
    }
}


struct MainButton: View {
    
    var color: Color
    var title: String
    var image: String
    
    @Binding var showSheet: Bool   // pass binding here
    
    var body: some View {
        
        Button {
            print("Opened")
            showSheet = true // set binding to show sheet
        } label: {
            Label {
                Text(title)
            } icon: {
                Image(systemName: image)
            }
            .padding()
            .frame(width: 350, height: 60)
            .background(color)
            .foregroundColor(.white)
            .cornerRadius(15)
            .font(.title2)
        }
    }
}

But it might be easier to change the buttons to vars or define custom Button styles.

Here is another variant with one CutomButton that is taking a button action as a closure, reducing the Binding passing around:

struct ContentView: View {
    
    @State private var showSheet = false // define state
    
    var body: some View {
        NavigationView {
            VStack {
                Spacer()
                Image(systemName: "hand.thumbsup.circle.fill")
                    .resizable()
                    .frame(width: 200, height: 200)
                    .symbolRenderingMode(.hierarchical)
                Spacer()
                CustomButton(color: .blue, title: "Tap to Open", image: "lock.open.laptopcomputer") {
                    showSheet = true // passed button action
               }
                .font(.title2)
                
            }
            .navigationTitle("Page One")
            
            .sheet(isPresented: $showSheet) { // present sheet
                SheetView(showSheet: $showSheet) // pass binding
            }
        }
    }
}



struct SheetView: View {
    
    @Binding var showSheet: Bool   // pass binding here
    
    var body: some View {
        NavigationView {
            VStack {
                Text("Hello, World!")
            }
            .navigationTitle("Sheet View")
            .navigationBarItems(
                trailing:
                    CustomButton(color: .red, title: "Close") {
                        showSheet = false // passed button action
                    }
            )
        }
    }
}


struct CustomButton: View {
    let color: Color
    let title: String
    var image: String? = nil

    let action: () -> Void // button action (as trailing closure)
    
    var body: some View {
        Button {
            action() // run the passed action
        } label: {
            Group {
                if let image {
                    Label(title, systemImage: image)
                } else {
                    Text(title)
                }
            }
            .padding()
            .background(color)
            .foregroundColor(.white)
            .cornerRadius(15)
        }
    }
}
  • Related