skip to Main Content

I’m encountering a strange behaviour with List when using section and either task or onAppear.

Let’s say I have a list with sections and rows in each section. When I put a task to run a async task when each row appears, it doesn’t call it even though it’s displayed on screen. The same problem applies when using onAppear instead of task.

It’s easily reproducible with the given example. You can just run and scroll down to the bottom. You’ll notice that the last task isn’t called despite the row and the section is on screen.

struct ContentView: View {
  private var dataSource: [Int: [String]] = (0..<30).reduce([Int: [String]]()) { result, key in
    var result = result
    let items = (0..<4).map { "Item ($0)" }

    result[key] = items

    return result
  }


  var body: some View {
    List {
      ForEach(Array(dataSource.keys), id: .self) { section in
        let rows = dataSource[section]

        Section {
          ForEach(rows ?? [], id: .self) { row in
            Text("(row)")
          }
          .task {
            print("TASK (section)")
          }
        } header: {
          Text("Section (section)")
        }

      }
    }
  }
}

Does anyone has an explanation ? Am I missing something ?

I managed to fix this problem by using a ScrollView which embeds a LazyVStack, but by doing so I’m loosing some of the features from List, such as swipe to delete.

2

Answers


  1. .task is when the underlying UIView appears, which in the case of List is a UICollectionViewCell and those only appear once and are reused when scrolling so have already appeared so .task won’t run again.

    Btw ForEach is not a for loop.

    Login or Signup to reply.
  2. I was found this solution on this way. May be help for you.

        private var dataSource: [Int: [String]] = (0..<30).reduce([Int: [String]]()) { result, key in
            var result = result
            let items = (0..<4).map { "Item ($0)" }
            result[key] = items
            return result
        }
        
        var body: some View {
            let arrData = dataSource.keys.sorted()
            List {
                ForEach(Array(arrData), id: .self) { section in
                    let rows = dataSource[section]
                    
                    Section {
                        ForEach(rows ?? [], id: .self) { row in
                            Text("(row)")
                        }
                        .task {
                            print("TASK (section)")
                        }
                    } header: {
                        Text("Section (section)")
                    }
                }
            }
        }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search