MacOS上的SwiftUi列表过早加载每一行

发布于 2025-01-23 04:15:25 字数 3645 浏览 3 评论 0 原文

我正在观察 swiftui list s上的奇怪问题(它们在iOS上确实可以按预期工作)。问题是 list 调用 init & 每个行的主体,即使这些行不在屏幕上(并且可能永远不会显示)。

如果我将 list 替换为 scrollview + lazyvstack ,它确实可以按预期工作(只有那些要渲染的行才能得到他们的<代码> init &amp; <代码> body 呼叫)。当然,这不是理想的解决方法,因为您将使用 list 的内置好处(主要是选择)。

我确实希望在引擎盖下, swiftui 将使用与 nstableView (按需加载单元格)相同的机制。从历史上看,我是iOS开发人员,所以我习惯了 cellforrowatIndExpath 来自 uitableView

这是一个快速的要点:

import SwiftUI

@main
struct SwiftUIListVSLazyVStackApp: App {

    let numbers = (0...100).map { $0 }

    var body: some Scene {
        WindowGroup {
            /*
             List calls the init & the body of *every* element on the list, even if it's not being displayed.
             This is unexpected because a tableView usually only loads the visible cells as needed (cellForRowAtIndexPath)
             */
            List(numbers) { number in
                RowView(for: number, example: "list")
            }

            /*
             A combination of ScrollView + LazyVStack achieves what's expected from the list. Only calls the init & body of
             the element that's going to be displayed.
             */
//            ScrollView {
//                LazyVStack(alignment: .leading, spacing: 8) {
//                    ForEach(numbers) { number in
//                        RowView(for: number, example: "stack")
//                    }
//                }
//            }
        }
    }
}

struct RowView: View {

    private let number: Int
    private let example: String

    init(for number: Int, example: String) {
        print("Init \(example): \(number)")
        self.number = number
        self.example = example
    }

    var body: some View {
        let _ = print("Body \(example): \(number)")
        Text("\(number)")
            .onAppear{ print("Appear \(example): \(number)") }
    }
}

extension Int: Identifiable {
    public var id: Int { self }
}

github gist:

如果您无法运行代码,则可以更好地显示问题的视频:

Twitter线程:

任何帮助都非常感谢!

I'm observing a weird issue with SwiftUI Lists on macOS (they do work as expected on iOS). The problem is that the List calls the init & body of every row, even if those rows are not on screen (and might never be shown).

If I replace the List with a ScrollView + LazyVStack, it does work as expected (only those rows which are going to be rendered get their init & body called). Of course, this is is not an ideal workaround because you loose the built-in benefits of using a List (mainly selection in my case).

I did expect that under the hood, SwiftUI would use the same mechanism as NSTableView (which loads cells on demand). Historically I'm an iOS dev, so I'm used to cellForRowAtIndexPath coming from UITableView.

Here's a quick gist demonstrating the issue:

import SwiftUI

@main
struct SwiftUIListVSLazyVStackApp: App {

    let numbers = (0...100).map { $0 }

    var body: some Scene {
        WindowGroup {
            /*
             List calls the init & the body of *every* element on the list, even if it's not being displayed.
             This is unexpected because a tableView usually only loads the visible cells as needed (cellForRowAtIndexPath)
             */
            List(numbers) { number in
                RowView(for: number, example: "list")
            }

            /*
             A combination of ScrollView + LazyVStack achieves what's expected from the list. Only calls the init & body of
             the element that's going to be displayed.
             */
//            ScrollView {
//                LazyVStack(alignment: .leading, spacing: 8) {
//                    ForEach(numbers) { number in
//                        RowView(for: number, example: "stack")
//                    }
//                }
//            }
        }
    }
}

struct RowView: View {

    private let number: Int
    private let example: String

    init(for number: Int, example: String) {
        print("Init \(example): \(number)")
        self.number = number
        self.example = example
    }

    var body: some View {
        let _ = print("Body \(example): \(number)")
        Text("\(number)")
            .onAppear{ print("Appear \(example): \(number)") }
    }
}

extension Int: Identifiable {
    public var id: Int { self }
}

GitHub gist: https://gist.github.com/xmollv/7ecc97d8118c100e85698c5ff09a20dc

And a video to better show the issue if you can't run the code:
https://gist.github.com/xmollv/7ecc97d8118c100e85698c5ff09a20dc?permalink_comment_id=4140623#gistcomment-4140623

Twitter thread: https://twitter.com/xmollv/status/1517158777882955779

Any help is very much appreciated!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文