通过观看WWDC2023关于ScrollView的视频,总结一下学习的知识点。
具体的使用方法可以参考下面的代码中的注释:
struct Item: Identifiable {
var id: Int
}
struct ScrollDemoView: View {
@State private var items: [Item] = (0 ..< 25).map { Item(id: $0) }
@State private var scrollId: Int?
var body: some View {
ScrollView(.horizontal) {
LazyHStack(content: {
ForEach(items) { item in
ItemView(item: item)
/// 设置宽高比
.aspectRatio(1.5, contentMode: .fit)
/// 相对于最近容器的布局:此处设置在容器中展示相同宽度的2个view,间隔为5.0
.containerRelativeFrame(.horizontal, count: 2, spacing: 5.0)
.clipShape(.rect(cornerRadius: 10))
/// 每个Item在可见和不可见之间切换时的动画
.scrollTransition(axis: .horizontal) { content, phase in
content
/// 缩放动画
.scaleEffect(
x: phase.isIdentity ? 1.0 : 0.8,
y: phase.isIdentity ? 1.0 : 0.8)
/// 旋转动画
/*
.rotationEffect(.degrees(phase.isIdentity ? 0.0 : 90))
*/
/// 偏移动画
/*
.offset(
x: phase.isIdentity ? 0.0 : -20,
y: phase.isIdentity ? 0.0 : 20
)
*/
}
}
})
/// 指定哪些视图是滚动目标, 此处是指定lazy stack中的每个主视图都被视为滚动目标
.scrollTargetLayout()
}
/// 指定scrollview content的边距,并不是scrollview本身的
.contentMargins(.horizontal, 40.0, for: .scrollContent)
/// 设置在指定方向的滚动行为
/// paging: 翻页滚动,一次滚动一页
/// viewAligned: 根据滑动速度滚动不同数量的滚动目标,每次停止时都是完整的展示一页内容。
.scrollTargetBehavior(.viewAligned)
.scrollIndicators(.never)
/// 滑动到的item的id。通过改变当前id,可以编程控制滚动位置
.scrollPosition(id: $scrollId)
.overlay(alignment: .center) {
PageControlView(items: items, scrollId: $scrollId)
.offset(y: 100)
}
}
}
struct ItemView: View {
var item: Item
var body: some View {
Text("\(item.id)")
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(.blue)
}
}
struct PageControlView: View {
var items: [Item]
@Binding var scrollId: Int?
var body: some View {
HStack {
Button {
scrollToPreviousID()
} label: {
Label("上一页", systemImage: "chevron.backward.circle")
.imageScale(.large)
.labelStyle(.iconOnly)
}
Spacer()
Button {
scrollToNextID()
} label: {
Label("下一页", systemImage: "chevron.forward.circle")
.imageScale(.large)
.labelStyle(.iconOnly)
}
}
.padding(20)
}
private func scrollToNextID() {
guard let id = scrollId, id != items.last?.id,
let index = items.firstIndex(where: { $0.id == id })
else { return }
withAnimation {
scrollId = items[index + 1].id
}
}
private func scrollToPreviousID() {
guard let id = scrollId, id != items.first?.id,
let index = items.firstIndex(where: { $0.id == id })
else { return }
withAnimation {
scrollId = items[index - 1].id
}
}
}

效果图