Home > Mobile >  Passing A Closure With Arguments To SwiftUI View
Passing A Closure With Arguments To SwiftUI View

Time:10-27

I am working on a SwiftUI project. I've created a custom button that I can pass a function to. This looks like the following.

Custom Button

struct CustomButton: View {
    let buttonTitle: String
    var function: () -> Void
    
    var body: some View {
        Button(action: {
            self.function()
        }, label: {
            Text(self.buttonTitle)
        }) // Button - Login
    } // View
}

In the view that uses this I can do the following.

struct NewView: View {
    var body: some View {
        CustomButton(buttonTitle: "Custom Button", function: myFunc)
    }
}

func myFunc() {
    print("My Custom Button Tapped")
}

This works really well.

What I want to do now is pass a parameter to the function. And I am having trouble with this. I tried the following.

struct CustomButton: View {
    let buttonTitle: String
    var function: (String) -> Void
    
    var body: some View {
        Button(action: {
            self.function() // I DON'T KNOW WHAT DO TO HERE.
        }, label: {
            Text(self.buttonTitle)
        }) // Button - Login
    } // View
}

struct NewView: View {
    var body: some View {
        CustomButton(buttonTitle: "Custom Button", function: myFunc(text: "Hello"))
    }
}

func myFunc(text: String) {
    print(text)
}

This does not work. When I call CustomButton I get the following error.

Cannot convert value of type '()' to expected argument type '() -> Void'

I also do not know what parameter to add to the self.function() call in the Button action.

Any help would be greatly appreciated.

CodePudding user response:

First, the simplest answer -- by enclosing myFunc(text: "Hello") in { }, you can turn it into a closure. Then, it can get passed to your original () -> Void declaration.

struct CustomButton: View {
    let buttonTitle: String
    let function : () -> Void
    
    var body: some View {
        Button(action: {
            self.function()
        }, label: {
            Text(self.buttonTitle)
        }) // Button - Login
    } // View
}

struct NewView: View {
    var body: some View {
        CustomButton(buttonTitle: "Custom Button", function: {
            myFunc(text: "Hello")
        })
    }
}

You could also use an @autoclosure to provide similar behavior without the { }, but you'd have to declare a custom init for your CustomButton:

struct CustomButton: View {
    let buttonTitle: String
    let function : () -> Void
    
    init(buttonTitle: String, function: @autoclosure @escaping () -> Void) {
        self.buttonTitle = buttonTitle
        self.function = function
    }
    
    var body: some View {
        Button(action: {
            self.function()
        }, label: {
            Text(self.buttonTitle)
        }) // Button - Login
    } // View
}

struct NewView: View {
    var body: some View {
        CustomButton(buttonTitle: "Custom Button", function: myFunc(text:"Hello"))
    }
}

Finally, another option (that I think there's unlikely to a use case for, but just in case it fits) would be to pass the string parameter separately:

struct CustomButton: View {
    let buttonTitle: String
    let stringParameter : String
    let function : (String) -> Void
    
    var body: some View {
        Button(action: {
            self.function(stringParameter)
        }, label: {
            Text(self.buttonTitle)
        }) // Button - Login
    } // View
}

struct NewView: View {
    var body: some View {
        CustomButton(buttonTitle: "Custom Button", stringParameter: "Hello", function: myFunc)
    }
}

CodePudding user response:

Here what you may looking for:

struct ContentView: View {
    
    let action: (String) -> Void = { value in print(value) }
    
    var body: some View {

        CustomButtonView(string: "print", valueToSend: "Hello World!", action: action)
        
    }
    
}

struct CustomButtonView: View {
    
    let string: String
    let valueToSend: String
    let action: (String) -> Void
    
    var body: some View {
        
        Button(string) {
            
            action(valueToSend)
            
        }
        
    }
    
}
  • Related