I have created a repo with one demonstration project
https://github.com/PavelKatunin/SectionsAndItems
The whole idea and requirements are described in the readme file.
Long story short: it is an infinity vertical feed, and I want to be able to count reliably how many sections and items in the feed a user has seen.
I have ideas for 3 versions of implementation of this functionality,
but I started with as it seemed at the moment the most straightforward one.
I wanted to track appearance and disappearance of Sections and Items by handling
onAppear and onDisappear callbacks.
And I found out that sometimes onDisappear is not called when a view actually disappears from the screen and also there is no logical order of the calls (as it seems to me).
So I wonder if I’m doing something wrong here?
Here is a log when I scroll through the feed:
SiwftUI event: Section 1 appeared
SiwftUI event: Item 1 appeared
SiwftUI event: Item 2 appeared
SiwftUI event: Item 1 disappeared
SiwftUI event: Item 3 appeared
SiwftUI event: Item 2 disappeared
SiwftUI event: Item 4 appeared
SiwftUI event: Section 2 appeared
SiwftUI event: Item 1 appeared
SiwftUI event: Item 4 disappeared
SiwftUI event: Item 3 disappeared
SiwftUI event: Item 2 appeared
SiwftUI event: Item 1 disappeared
SiwftUI event: Item 3 appeared
SiwftUI event: Section 1 disappeared
SiwftUI event: Section 3 appeared
SiwftUI event: Item 1 appeared
SiwftUI event: Item 3 disappeared
SiwftUI event: Item 2 disappeared
SiwftUI event: Item 2 appeared
SiwftUI event: Item 1 disappeared
SiwftUI event: Item 3 appeared
SiwftUI event: Section 2 disappeared
SiwftUI event: Item 2 disappeared
SiwftUI event: Item 4 appeared
Here is my 3 simple views:
struct ItemView: View {
@State var text: String
var body: some View {
ZStack {
Text("(text)").font(.title)
}
.onAppear() {
print("SiwftUI event: Item (text) appeared")
}
.onDisappear() {
print("SiwftUI event: Item (text) disappeared")
}
}
}
struct SectionView: View {
@State var color: Color
@State var items: [String]
var sectionId: String
var body: some View {
LazyVStack(spacing: 0) {
ForEach(items, id: .hashValue) { item in
ItemView(text: item)
.frame(height: UIScreen.main.bounds.size.height)
}
}
.onAppear() {
print("SiwftUI event: Section (sectionId) appeared")
}
.onDisappear() {
print("SiwftUI event: Section (sectionId) disappeared")
}
.background(color)
}
}
struct ScrollOffsetFeedView: View {
static func createLocalSections() -> [Section] {
return [Section(id: "1", color: .green, items: ["1", "2", "3", "4"]),
Section(id: "2", color: .red, items: ["1", "2", "3"]),
Section(id: "3", color: .blue, items: ["1", "2", "3", "4", "5"])]
}
@State var sections: [Section]
init(sections: [Section]) {
self.sections = sections
}
var body: some View {
ZStack {
ScrollView {
LazyVStack(spacing: 0) {
ForEach(sections, id: .id) { section in
SectionView(color: section.color,
items: section.items,
sectionId: section.id)
}
}
.scrollTargetLayout()
}
.scrollTargetBehavior(.paging)
.ignoresSafeArea()
.scrollIndicators(.hidden)
VStack {
ZStack {
makeSectionsCounter()
.background(Color(white: 0.0, opacity: 0.5))
.cornerRadius(10)
.padding()
}
Spacer()
}
}
}
@ViewBuilder func makeSectionsCounter() -> some View {
ZStack {
Text("Sections completed 0, item 1 / 4")
}
.padding()
}
}
How does onAppear and onDisappear work in ScrollView and LazyVStack?