I'm working on the SwiftUI, and feeling it's very similar with React. Just now I'm customizing a Button of SwiftUI and have a problem which can't access to the children views of Button dynamically Following codes is what I'm going to do:
struct FullButton : View {
var action: () -> Void
var body: some View {
Button(action: action) {
// render children views here even what is that
children
}
}
}
and usage:
VStack {
FullButton(action: {
print('touched')
}) {
Text("Button")
}
}
Please, do I have a wrong idea?
Update
Depends on @graycampbell 's answer I tried as following
struct FullButton<Label> where Label : View {
var action: () -> Void
var label: () -> Label
init(action: @escaping () -> Void, @ViewBuilder label: @escaping () -> Label) {
self.action = action
self.label = label
}
var body: some View {
Button(action: action, label: label)
}
}
So the FullButton
looks well as itself. But I have another compile error in usage at this time.
VStack {
FullButton(action: { print("touched") }) {
Text("Fullbutton")
}
}
The error is Referencing initializer 'init(alignment:spacing:content:)' on 'VStack' requires that 'FullButton<Text>' conform to 'View'
.
It means FullButton
hasn't return the body
now?
I'm not sure why it is because the FullButton
still extends View
class.
Please let me know what's the correct body
definition of that type of class.
This is what you're looking for, if I'm understanding your question correctly:
This would allow you to pass whatever content you want to be displayed on your button, meaning that the code you have here would now work:
Update
After looking over your question several times, I've realized that your confusion is stemming from a misunderstanding of what is happening when you create a normal
Button
.In the code below, I'm creating a
Button
. The button takes two arguments -action
andlabel
.If we look at the documentation for
Button
, we see that it is declared like this:If we then look at the initializers, we see this:
Both
action
andlabel
expect closures.action
expects a closure with a return type ofVoid
, andlabel
expects a@ViewBuilder
closure with a return type ofLabel
. As defined in the declaration forButton
,Label
is a generic representing aView
, so really,label
is expecting a closure that returns aView
.This is not unique to
Button
. TakeHStack
, for example:Content
serves the same purpose here thatLabel
does inButton
.Something else to note - when we create a button like this...
...we're actually doing the same thing as this:
In Swift, when the last argument in a method call is a closure, we can omit the argument label and append the closure to the outside of the closing parenthesis.
In SwiftUI, you cannot implicitly pass content to any
View
. TheView
must explicitly accept a@ViewBuilder
closure in its initializer.And so, you cannot pass a
@ViewBuilder
closure toFullButton
unlessFullButton
accepts a@ViewBuilder
closure as an argument in its initializer, as shown at the beginning of my answer.