4

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

easer liu
  • 43
  • 4
  • 1
    show a minimal reporducible code example, https://stackoverflow.com/help/minimal-reproducible-example not the bits of code that you are showing us. In particular, show the code for, and explain `imgContent`, similarly is `loadCachedImg()` an async function? If not why not use `.onAppear{...}` – workingdog support Ukraine Sep 22 '22 at 04:18
  • Thanks for your suggestion. Already update code.`loadCachedImg()` is not the reason, the reason is `task{}` wasn't called, `onAppear{}` and `onReceive` also have the same problem. @workingdogsupportUkraine – easer liu Sep 22 '22 at 06:13
  • your code seems to work for me (the print is executed), on macos 13 Ventura, using xcode 14.1-beta, target macCatalyst and ios-16. Tested on real devices, not Previews. It may be different on older systems. – workingdog support Ukraine Sep 22 '22 at 06:17
  • The problem first happen on real device(iPhone8 iOS16.0) before, then test again on simulator. Xcode14.0, macOS 12.6. maybe need to update macOS and Xcode? by the way, how is your scrolling speed. I scroll fast @workingdogsupportUkraine – easer liu Sep 22 '22 at 06:43
  • with `.task{...}` if I scroll fast there are times when the `print` does not show. If I use `.onAppear{..}` it seems to print all expected values. – workingdog support Ukraine Sep 22 '22 at 07:00
  • well, on my side, `.onAppear{}` has the same problem. However, Thanks for your help.@workingdogsupportUkraine – easer liu Sep 22 '22 at 08:56
  • I see a mistake. `@State var name` should be `let name` – malhal Sep 22 '22 at 09:01
  • @malhal Maybe it's a mistake `@State var name`, but it's not the reason of this problem, I tried. – easer liu Sep 22 '22 at 09:08

1 Answers1

1

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

Note2: I believe this workaround doesn't work when using Sections that contain the same cells.

malhal
  • 26,330
  • 7
  • 115
  • 133