如何在SwiftUI中把列表滚动到最顶部?

移动开发 2026-07-09

我尝试了各种技巧,但仍无法在SwiftUI中带有头部的列表滚动到最顶端的位置。下面给出一个最小可复现示例(MRE)。它能滚动到大致的顶部位置,但并不是用户通过手指滑动能达到的真正最顶端的位置。

我不想使用ScrollView或其他东西。我其实需要的是List。

struct StackScrollToTopExample: View {
    private struct ExampleItem: Identifiable {
        let id = UUID()
        let title: String
    }

    private let items = [
        ExampleItem(title: "Passport"),
        ExampleItem(title: "Wallet"),
        ExampleItem(title: "Phone charger"),
        ExampleItem(title: "Headphones"),
        ExampleItem(title: "Sunglasses"),
        ExampleItem(title: "Toothbrush"),
        ExampleItem(title: "Toothpaste"),
        ExampleItem(title: "Socks"),
        ExampleItem(title: "T-shirt"),
        ExampleItem(title: "Jeans"),
        ExampleItem(title: "Jacket"),
        ExampleItem(title: "Book"),
        ExampleItem(title: "Camera"),
        ExampleItem(title: "Medication"),
        ExampleItem(title: "Water bottle"),
        ExampleItem(title: "Travel adapter"),
        ExampleItem(title: "Umbrella"),
        ExampleItem(title: "Notebook"),
        ExampleItem(title: "Snacks"),
        ExampleItem(title: "Keys"),
    ]

    var body: some View {
        NavigationStack {
            ScrollViewReader { proxy in
                List {
                    Section {
                        ForEach(items) { item in
                            Text(item.title)
                                .id(item.id)
                        }
                    } header: {
                        Text("Header")
                    }
                }
                .toolbar {
                    ToolbarItem(placement: .topBarTrailing) {
                        Button("Top") {
                            guard let firstItemID = items.first?.id else { return }

                            withAnimation {
                                proxy.scrollTo(firstItemID, anchor: .top)
                            }
                        }
                    }
                }
            }
        }
    }
}

解决方案

之所以它不会自动把列表一直滚回到起始位置,是因为列表分区有一个头部。因此第一行的起始位置实际上位于 .top 之下。如果你用锚点 .top 滚动到第一行,列表会向上滚动一点。

为了解决,我建议给头部设置一个id,然后滚动到这个目标。然而,我发现使用锚点 .top 时这仍然不太可靠。把锚点改为 .center 会更可靠:

private let headerId = "HEADER"
List {
    Section {
        ForEach(items) { item in
            Text(item.title)
                // .id(item.id) 👈 not needed
        }
    } header: {
        Text("Header")
            .id(headerId) // 👈 id added
    }
}
.toolbar {
    ToolbarItem(placement: .topBarTrailing) {
        Button("Top") {
            withAnimation {
                proxy.scrollTo(headerId, anchor: .center) // 👈 updated
            }
        }
    }
}
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章