SwiftUI遍历数组时如何添加过渡动画?
问题原因与解决方案
你的代码里.transition(.scale)无效的核心原因是:过渡动画仅作用于视图的添加/移除操作,而当前你只是修改currentIndex来切换同一个Text控件的内容,并没有触发视图的添加或移除,所以.transition不会生效。另外你设置的Timer间隔是1.5秒,和需求的7.5秒不符,这也需要调整。
以下是修复后的代码,实现通知切换时的淡入淡出+位移过渡,同时匹配你的7.5秒切换需求:
struct SoleInAppNotifications3: View { @State private var currentIndex: Int = 0 @State private var isPaused: Bool = false @State var hasImage: Bool? = true @State var isPressed: Bool = false @State var isDismissed: Bool = false private var notifications: [NotificationItem] = [ NotificationItem(title: "Notification 1", subtitle: "Notification 1 subtitle"), NotificationItem(title: "Notification 2", subtitle: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, se"), NotificationItem(title: "Notification 3", subtitle: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, se") ] // 调整为7.5秒切换一次通知 let timer = Timer.publish(every: 7.5, on: .main, in: .common).autoconnect() var body: some View { HStack(spacing: .zero) { if hasImage ?? false { Image("raiff") .padding(.trailing, 17) } // 用ZStack叠加所有通知视图,通过属性变化实现过渡 ZStack(alignment: .leading) { ForEach(notifications.indices, id: \.self) { index in VStack(alignment: .leading, spacing: .zero) { Text(notifications[index].title) .foregroundColor(.white) .lineLimit(1) .padding(.bottom, 1) Text(notifications[index].subtitle) .foregroundColor(.white) .lineLimit(2) } .opacity(currentIndex == index ? 1 : 0) .offset(y: currentIndex == index ? 0 : 10) .animation(.easeInOut(duration: 0.5), value: currentIndex) } } Spacer() VStack(spacing: .zero) { ForEach(0..<notifications.count, id: \.self) { index in bubbleView(isActive: index == currentIndex) } } } .frame(maxWidth: .infinity) .padding(17) .background(isPressed ? Color.red : Color.black) .onReceive(timer) { _ in withAnimation { // 实现通知循环切换 currentIndex = (currentIndex + 1) % notifications.count } } } private func bubbleView(isActive: Bool) -> some View { ZStack { if isActive { RoundedRectangle(cornerRadius: 3) .fill(Color.gray) .frame(width: 6, height: 24) .overlay( RoundedRectangle(cornerRadius: 3) .trim(from: 0, to: progress()) .foregroundColor(Color.white) .animation(.easeInOut(duration: 7.5), value: currentIndex) ) } else { Circle() .fill(Color.gray) .frame(width: 6, height: 6) .padding(4) .animation(.easeInOut, value: currentIndex) } } } // 修复进度计算逻辑,基于当前通知的显示时长生成进度 func progress() -> Double { guard let lastFire = timer.lastFireDate else { return 0 } let elapsed = Date().timeIntervalSince(lastFire) return min(elapsed / 7.5, 1) } } // 补充NotificationItem结构体(假设你已有该定义) struct NotificationItem: Identifiable { let id = UUID() let title: String let subtitle: String }
关键修改说明:
- 过渡动画实现:使用
ZStack叠加所有通知视图,通过opacity(透明度)和offset(位移)的变化配合animation修饰符,实现切换时的淡入淡出+上滑效果,替代原来无效的.transition。 - Timer调整:将Timer间隔改为7.5秒,匹配需求。
- 进度条逻辑修复:重新实现
progress()函数,基于当前通知的实际显示时长计算进度,让激活状态的气泡进度条能正确从0到1填充。 - 循环切换优化:用
(currentIndex + 1) % notifications.count实现通知的循环切换,简化原来的判断逻辑。
如果需要其他过渡效果(如缩放、左右滑入),只需修改opacity和offset的参数,或替换为scaleEffect、offset(x:)等属性即可。
内容的提问来源于stack exchange,提问作者user20273483
相关产品推荐
相关产品推荐

