@chriseidhof Once the view has been removed its ordering relative to other views is lost, so it’s placed at the bottom of the stack. You can use zIndex() to give views a persistent ordering. It’s def not ideal, but doing better is hard, especially if more than one insertion/removal happens.
@anandabits @jckarter We recursively walk the type, using the reflection metadata to find each field or enum payload and their type + offset/tag. That builds a description of how to compare two values.
@jckarter @dimsumthinking … And typically you only need one Lazy container per scroll view, but we don't have a good way to tell *which* one if your scroll view contains multiple stacks etc. So it seemed better to make it unambiguous and require the lazy behavior be enabled explicitly.
@jckarter @dimsumthinking the Lazy containers use a simpler model to let them be as efficient as possible: they don't fully measure views in the minor-axis or support complex minor-axis alignment. They also assume a flexible major axis, so don't try to pack views into a fixed space …
@SwiftUILab@helje5 When it does the per-field comparison the same rules are applied recursively to each field (to choose direct comparison or == if defined). (POD = plain data, see Swift’s _isPOD() function.)
@SwiftUILab@helje5 SwiftUI assumes any Equatable.== is a true equality check, so for POD views it compares each field directly instead (via reflection). For non-POD views it prefers the view’s == but falls back to its own field compare if no ==. EqView is a way to force the use of ==.
@twostraws@mecid There’s persistent data built from the view *type* tree. This is cheaply updated when view values change. If a type changes, the persistent data has to be partially destroyed and recreated, which can cost more. That’s the perf hit of AnyView(x) vs “x” (+ memory to track it all).
@twostraws@mecid I was really describing AnyView(x) vs plain “x”. AnyView and if-else do (currently!) have a similar implementation so I’d expect them to have similar perf. I’ll try to clarify why AnyView adds overhead …
@twostraws Those are very different. Group should always be cheaper as AnyView can’t optimize for the type of its child (needs extra indirection), while Group is a pass-through (so very cheap). Whether it’s significant probably depends on how many instances there are.
@chriseidhof @dgregor79 @jckarter You can see this by doing `TupleView(1)`. 1’s not a view, we can’t prevent that compiling, but that’s ok as TupleView is not really API. But you should see a warning logged saying that it’s ignoring that element as Int doesn’t conform to View.
@chriseidhof @dgregor79 @jckarter TupleView calls swift_conformsToProtocol() once with the metadata of each element and the protocol descriptor for View. If that says it’s a view it calls a generic swift function to forward the type. It’s a bit like hand-coded generalized existentials and _openExistential()!
@chriseidhof @dgregor79 @jckarter No, types are preserved throughout. AnyView is complex as it’s not possible to know statically how many “real views” an instance translates to. So it’s really the opposite: AnyView is turned back into a concrete type asap 😀