Something that I noticed today and didn't expect it is that if a Group
is not the root view of the screen, its onAppear
is called per each child, while for a Group that is the root, the method is called once, regardless of the number of its children. For example:
struct ContentView: View {
var body: some View {
Group { // Group is the root of the screen, onAppear is called once
Group { // non-root view, onAppear is called once per each child
Color(.red)
Color(.yellow)
}
.onAppear { print(".:. onAppear2") }
.onDisappear { print(".:. onDisappear2") }
Color(.blue)
Color(.purple)
}
.onAppear { print(".:. onAppear1") }
.onDisappear { print(".:. onDisappear1") }
}
}
/* Output:
.:. onAppear1
.:. onAppear2
.:. onAppear2
*/
It becomes more interesting when you learn that if the combination of the children of a non-root Group changes while the view is being displayed, onDisappear
is called for each child that gets removed and onAppear
is called when a new child gets added. For example, in the next sample code, within the non-root Group, there is a condition (counter.counter == nil
) that at the beginning is true
, so the Group has 3 children and onAppear
is called 3 times. After 1 second, the condition becomes false
and the first child gets removed, and interestingly onDisappear
of Group is called!
class Counter: ObservableObject {
@Published var counter: Int? = nil
init() {
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
self.counter = 1
}
}
}
struct IceCreamListView: View {
@ObservedObject private var counter = Counter()
var body: some View {
Group {
Group {
if counter.counter == nil {
Color(.red)
}
Color(.red)
Color(.yellow)
}
.onAppear { print(".:. onAppear2") }
.onDisappear { print(".:. onDisappear2") }
Color(.blue)
Color(.purple)
}
.onAppear { print(".:. onAppear1") }
.onDisappear { print(".:. onDisappear1") }
}
}
/* Output:
.:. onAppear1
.:. onAppear2
.:. onAppear2
.:. onAppear2
.:. onDisappear2
*/
I have tested these pieces of code on:
Xcode Version 11.4.1 (11E503a)
Xcode Version 11.5 (11E608c)