Home > Net >  List row task / onAppear not called
List row task / onAppear not called

Time:12-23

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.

CodePudding user response:

.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.

CodePudding user response:

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)")
                }
            }
        }
    }
}
  • Related