SwiftUI自动定时分页ScrollView无法正常轮播跳转问题
问题根因
自动滚动失效和Identifiable协议、自定义模型数组没有任何关系,是两处基础逻辑错误导致的:
- 分页总数硬编码错误:你将
private var amount赋值为1,但实际items数组里有4张卡片。定时器触发时的边界判断currentIndex < amount-1等价于currentIndex < 0,条件永远不成立,每次触发定时器都会直接把currentIndex重置为0,自然只会尝试滚动到第一页。 - 视图ID重复:你错误嵌套了两层
ForEach,外层遍历全部4个items,内层仅遍历0..<amount(即只有0一个值),最终会生成4个id都为0的卡片。ScrollViewReader要求滚动目标的id在滚动视图内全局唯一,出现重复ID时代理只会定位到第一个匹配ID的视图,也就是你看到的第一张卡片。 - 额外提一句,视图里声明的
var item: Item = items[0]是冗余无效属性,内层ForEach闭包里的item参数会直接覆盖这个属性,留着只会干扰调试。
你之前换成固定字符串能正常运行,本质是写测试代码时没有嵌套多余的ForEach,且amount值和实际卡片数匹配,和用不用模型数组无关。
修复方案
- 不要硬编码
amount,直接取items数组的实际长度作为分页总数 - 删除多余的嵌套
ForEach,直接遍历模型数组,给每个卡片绑定唯一的ID - 保持定时器的边界判断逻辑和实际卡片总数一致
修复后的核心代码如下:
struct TestView: View { // 分页总数直接绑定items数组长度,避免硬编码不一致 private var amount: Int { items.count } private let timer = Timer.publish(every: 5, on: .main, in: .common).autoconnect() @State private var currentIndex = 0 var body: some View { ZStack { ScrollViewReader { scrollProxy in SnappingScrollView(.horizontal, decelerationRate: .fast, showsIndicators: false) { // 移除多余的外层嵌套循环,直接遍历模型数组 ForEach(Array(items.enumerated()), id: \.element.id) { index, item in GeometryReader { geometry in ZStack { Rectangle() .foregroundColor(Color.black.opacity(0.2)) .blur(radius: 7) .frame(maxWidth: 350, maxHeight: 300, alignment: .center) .cornerRadius(16) .shadow(color: Color.black.opacity(0.2), radius: 20, x: 0, y: 0) .offset(x: 15, y: 50) .rotation3DEffect(Angle(degrees: (Double(geometry.frame(in: .global).minX) - 30) / -20), axis: (x: 0, y: 1.0, z: 0)) Rectangle() .stroke(lineWidth: 50) .foregroundColor(.gray) .blur(radius: 100) .frame(maxWidth: 350, maxHeight: 300, alignment: .center) .cornerRadius(16) .shadow(color: Color.black.opacity(0.3), radius: 1) .offset(x: 15, y: 50) .rotation3DEffect(Angle(degrees: (Double(geometry.frame(in: .global).minX) - 30) / -20), axis: (x: 0, y: 1.0, z: 0)) VStack { Text(item.title) .font(.title2) .fontWeight(.bold) .padding(.bottom, 10) Text(item.description) .font(.system(size: 16)) Image(item.image) .resizable() .frame(width: 90, height: 90) .cornerRadius(25) .padding(.bottom, -30) .padding(.top) } .frame(width: 300) .multilineTextAlignment(.center) .offset(y: 20) .rotation3DEffect(Angle(degrees: (Double(geometry.frame(in: .global).minX) - 30) / -20), axis: (x: 0, y: 1.0, z: 0)) .padding(.leading, 25) .padding(.top, 70) } } .frame(width: 400, height: 500) .offset(x: 12) // 绑定当前索引作为唯一ID .id(index) .scrollSnappingAnchor(.bounds) } } .onReceive(timer) { _ in currentIndex = currentIndex < amount - 1 ? currentIndex + 1 : 0 withAnimation { scrollProxy.scrollTo(currentIndex) } } } } } }
模型部分的代码不需要做任何改动,直接用原来的Item结构体和items数组即可。
内容的提问来源于stack exchange,提问作者user17136166
相关产品推荐
相关产品推荐

