Hello everyone!
I read that AnyView can impact app performance in a negative way because per Apple's documentation: Whenever the type of view used with an AnyView changes, the old hierarchy is destroyed and a new hierarchy is created for the new type.
struct MyView: View {
@State flag: Bool
var body: some View {
AnyView(
flag ? Text("Text") : Button("Button")
)
}
}
So when the flag is toggled, the type of the view changes, so the new hierarchy is created. That makes sense to me.
But if I write the code this way.
struct MyView: View {
@State flag: Bool
var body: some View {
flag ? AnyView(Text("Text")) : AnyView(Button("Button"))
}
}
Technically, the type of the view wrapped by AnyView doesn't change when the flag is toggled. Would that still cause the whole view hierarchy to be created and redrawn?
Your first example has a few syntax errors. Here's a corrected version:
struct MyView: View {
@State var flag: Bool
var body: some View {
AnyView(
flag ? Text("Text") : Button("Button") { }
)
}
}
However, it still doesn't compile. The error is
Result values in '? :' expression have mismatching types 'Text' and 'Button<Text>'
Your second example has the same syntax errors. Here's a corrected version:
struct MyView: View {
@State var flag: Bool
var body: some View {
flag ? AnyView(Text("Text")) : AnyView(Button("Button") { })
}
}
This version does compile.
You said
Technically, the type of the view wrapped by AnyView doesn't change when the flag is toggled.
But that is not true. The type of view wrapped my AnyView is Text when flag is true, and Button<Text> when flag is false. So whet flag changes from true to false, SwiftUI presumably has to discard the backing structures of Text, and create backing structures for Button<Text>.
However, although I've also heard the Apple advice about avoiding it, all the anecdotal third-party reports I've heard say that the overhead of AnyView is not normally significant.
Consider this view:
struct MyAdviceFollowingView: View {
@State var flag: Bool
var body: some View {
if flag {
Text("Text")
} else {
Button("Button") { }
}
}
}
This view's body returns a value of type _ConditionalContent<Text, Button<Text>>. This type tells SwiftUI that the only possible backing structures it might need here are for Text and for Button<Text>. Maybe this lets it be more efficient about memory allocation for the appropriate structures. But when flag changes from true to false, SwiftUI still has to at least set up the backing structures for Button<Text>; that work is unavoidable. Probably it also discards the structures it had for Text; otherwise, it's still using memory (and maybe window server resources?) to keep them around when they might never be needed again.
Thank you for correcting the syntax errors. I might have interpreted their documentation wrong. That makes sense now.
Yeah I'd love to avoid AnyView since I am not sure how much of the performance impact would be but in my use case, there is no better option.
Because some View can't be used as a return type in protocol, I need something I can use, and AnyView is the only option