I am having a problem passing functions into a function as a parameter. I want to pass the function 'addOne()' (at the bottom of the below code) into the 'buttonView' function above it. The parameters relate to a button (there are five in the full code), and change the color, the title, and finally the action of the button. In this case it is meant to add 1 to the score. Can someone assist me please?
Hi Jon and thanks for helping. There are 5 functions that the 5 buttons use (all below). As an example, button 1 will have a label "+1" on it, it will be red, and action the first func below. The second button will have label "+2", be yellow, and perform the second func below, and so on. I can get the labels and colours ok, but I can't get how to pass the below funcs into the main button func as parameters.
The type of each of those functions is () -> Void (a function that takes zero parameters and returns nothing). So, that’s what you’d put as the type of the action parameter:
Got it thanks. Can you tell me what I then put in the Button braces to activate action? I have tried inserting 'action', (action), and 'action()', but none work. Again thanks for your help
Not just "don't work", they are telling you: Passing non-escaping parameter 'action' to function expecting an @escaping closure
or Escaping closure captures non-escaping parameter 'action'
If you were using the view like I written above "escaping" attribute is assumed:
struct ButtonView: View {
let name: String
let color: Color
let execute: () -> Void
...
}
ButtonView(..., execute: action) or
ButtonView(...) { action() }
The way you have it is ok, just there is no great need for the internal variable, it could be just:
func buttonView(name: String, color: Color, action: @escaping () -> Void) -> some View {
Button(action: action) {
...
}
}
// i am simplifying, in your case it is ZStack
You probably figured out the @escaping part yourself and are wondering why you don’t see anything change when it calls your function. What you are likely running into is less of a Swift issue and more of an understanding how SwiftUI draws Views issue.
For example, put this in Swift Playgrounds and run it and you’ll see your button and a number of times it’s been tapped above it, but when you tap on it the number stays at zero, despite indications that your addOne function runs every tap.
The issue here is that SwiftUI doesn’t know the score changed, so it isn’t redrawing. Now, if your model was in an actual @ObservableObject and your score @Published so SwiftUI can see changes made to the model, then it would know when to redraw.
For example, add this to the playground and you’ll see the score updates are reflected in the View.
class Scores: ObservableObject {
@Published var score1: Int = 0
func addOne() {
score1 += 1
}
}
struct DynamicContentView: View {
@ObservedObject var model = Scores()
var body: some View {
VStack {
Text("\(model.score1)")
button(name: "add", color: .primary) { model.addOne() }
}
}
}
PlaygroundPage.current.setLiveView(DynamicContentView())
I very highly recommend watching the Stanford CS193p class that’s available for free on YouTube and it will teach you a great deal about SwiftUI.