Reputation: 4308
I need to have a VStack
as a property in a struct like this
struct Str
{
let view: VStack
....
}
The compiler says:
"Reference to generic type 'VStack' requires arguments in <...>
Insert '<<#Content: View#>>'
"
Xcodes "fix" produces:
struct Str
{
let view: VStack<Content: View>
...
}
But the compiler still complains:
"Expected '>' to complete generic argument list"
Is it allowed to have a VStack in a struct?
Upvotes: 1
Views: 934
Reputation: 385870
What you have discovered is that VStack
is not really a type. We can more accurately describe it as a “type constructor”. It is declared like this:
struct VStack<Content> where Content : View {
...
}
You give it some other type as its Content
argument, and it gives you a type. You give it the type Text
and you get back the type VStack<Text>
. You give it the type Image
and you get back the type VStack<Image>
. The types you get back are different types! A object of type VStack<Text>
is not an object of type VStack<Image>
.
So you just need to know what type to pass as the argument, right? Well, the problem is that lots of SwiftUI “types” are actually generic type constructors. So the type gets complex. Here's an example from an app I'm working on:
var body: some View {
VStack(spacing: 0) {
scene
if store.model.latestError != nil {
Divider()
ErrorView(store: errorStore)
.padding(20)
}
} //
.frame(width: 450)
}
What's the type of body
? I can print it at runtime to find out:
print(type(of: AppView(store: store).body))
The output is
ModifiedContent<VStack<TupleView<(AnyView, Optional<TupleView<(Divider, ModifiedContent<ErrorView, _PaddingLayout>)>>)>>, _FrameLayout>
Notice that the type of the object returned by body
exposes lots of details about body
's implementation. For example, because the ErrorView
has a padding
modifier, the body
object's type includes ModifiedContent<ErrorView, _PaddingLayout>
. I can change the padding amount from 20 to 30 without changing the type. But if I remove the padding
modifier entirely, it changes the type (by changing ModifiedContent<ErrorView, _PaddingLayout>
to just ErrorView
).
So how do you solve this? One way is to use AnyView
:
struct Str {
let view: AnyView
...
}
let str = Str(view: AnyView(VStack {
...
}))
Note that sometimes you will hear advice that you should avoid AnyView
. Here's Joe Groff explaining why:
You can use the AnyView wrapper to explicitly wrap up types where they can dynamically change.
However, the animation system will have an easier time if you can push this down the view graph as far as possible. You could have one Layout type that takes
.portrait
/.landscape
, apply rotation, translation, etc. differently in response, and then the transition can animate
Upvotes: 4