Home > Net >  While scroll the swiftUI list, cell's task modifier wasn't called. How to fix?
While scroll the swiftUI list, cell's task modifier wasn't called. How to fix?

Time:09-23

I have written a AsyncImgView with swiftui, and used in a list cell.

AsyncImgView use task modifier to download Img from cache or net when it appears, before iOS16 everything is ok, but in iOS16 I found that while I scroll the list the new cell's AsyncImgView doesn't call task modifier, so new AsyncImageView won't download Img unless touch it again. And I found some other UI independent modifier has same problem.

I had tried call download func in body instead of using in task modifier, it works, but I don't think it's a good idea.

Anyone has any idea?

Demo code:

when scroll list, cann't print all cell name in task modifier

struct TaskDemo: View {
    var models: [TaskModel] = {
        var m = [TaskModel]()
        for idx in 0...99 {
            m.append(TaskModel(id: idx))
        }
        return m
    }()
    var body: some View {
        List(models){model in
            TaskCell(name: model.name)
                .frame(height: 100)
        }
    }
}

struct TaskModel: Identifiable {
    var id: Int
    var name: String { String(id) }
}

struct TaskCell: View {
    @State var name: String
    var body: some View{
            Text(name)
            .task {
                print("task default modifier \(Thread.current) \(name)")
            }
    }
}

the pic can show that I scroll to 31 cell but only print the first 17 cells' name enter image description here

CodePudding user response:

I think it's because List creates and updates UICollectionViewCells and it reuses them. So when you scroll, the cell is not disappearing and re-appearing it is simply being shifted around the screen. Since task runs when the view appears it doesn't happen because the cell already appeared and was just moved. Clearly SwiftUI is doing some optimisation here however you can override it with the use of .equatable and Equatable, e.g.

struct TaskModel: Identifiable {
    let id: Int
    var name: String { String(id) }
}

struct TaskDemo: View {
    let models = (0...99).map { TaskModel(id: $0) }

    var body: some View {
        List(models) { model in
            TaskCell(name: model.name)
                .equatable()
                .frame(height: 100)
        }
    }
}

struct TaskCell: View, Equatable {
    let name: String

    var body: some View{
            Text(name)
            .task {
                print("task default modifier \(Thread.current) \(name)")
            }
    }
}

Note: before iOS 16 List used UITableViewCell

  • Related