✅ 下拉刷新可以在 Banner 上方触发,会显示系统自带 loading。
✅ Banner 始终在最上方(随列表一起滚动,不被遮挡)。
✅ Section header 会悬浮在 Banner 下方,滚动时固定。
✅ 依旧支持上拉加载更多、底部提示。
importSwiftUI
struct Item: Identifiable {
letid=UUID()
lettitle:String
}
struct SectionData: Identifiable {
letid=UUID()
letheader:String
varitems: [Item]
}
struct ContentView: View {
@Stateprivatevarsections: [SectionData] = [
SectionData(header:"分组1",items: (1...10).map{Item(title:"分组1-Item\($0)") }),
SectionData(header:"分组2",items: (1...10).map{Item(title:"分组2-Item\($0)") })
]
@State private var isLoadingMore = false
@State private var hasMoreData = true
varbody:someView{
ScrollView{
LazyVStack(alignment: .leading,pinnedViews: [.sectionHeaders]) {
// ✅ Banner 放在 ScrollView 里面
bannerView
.frame(height:100)
.frame(maxWidth: .infinity)
.background(Color.blue)
// ✅ Sections 悬浮
ForEach(sections) { sectionin
Section{
ForEach(section.items) { itemin
Text(item.title)
.padding(.horizontal)
.padding(.vertical,6)
.onAppear{
ifsection.id ==sections.last?.id,
item.id == section.items.last?.id {
loadMore()
}
}
Divider()
}
}header: {
Text(section.header)
.font(.headline)
.frame(maxWidth: .infinity,alignment: .leading)
.padding()
.background(.ultraThinMaterial)// 关键:悬浮时不透明
}
}
// ✅ 底部提示
ifisLoadingMore{
HStack{
Spacer()
ProgressView("加载中...")
Spacer()
}
.padding()
}elseif!hasMoreData{
HStack{
Spacer()
Text("没有更多数据了")
.foregroundColor(.secondary)
.font(.footnote)
Spacer()
}
.padding()
}
}
}
// ✅ 下拉刷新在 Banner 上方触发
.refreshable{
awaitrefreshData()
}
}
private var bannerView: some View {
VStack{
Text("固定在顶部的 Banner")
.foregroundColor(.white)
.font(.title2)
}
}
// 下拉刷新
private func refreshData() async {
try?awaitTask.sleep(nanoseconds:1_000_000_000)
sections= [
SectionData(header:"分组1",items: (1...10).map{Item(title:"刷新后-分组1-Item\($0)") }),
SectionData(header:"分组2",items: (1...10).map{Item(title:"刷新后-分组2-Item\($0)") })
]
hasMoreData=true
}
// 上拉加载更多
private func loadMore() {
guard !isLoadingMore, hasMoreData else { return }
isLoadingMore = true
DispatchQueue.main.asyncAfter(deadline: .now() +1.5) {
ifvarlast =sections.last{
letstart = last.items.count+1
letmoreItems = (start..
Item(title:"\(last.header)-Item\($0)")
}
last.items.append(contentsOf: moreItems)
sections[sections.count-1] = last
iflast.items.count>=30{
hasMoreData=false
}
}
isLoadingMore=false
}
}
}
#Preview {
NavigationStack {
ContentView()
}
}