You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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
}

关键修改说明:

  1. 过渡动画实现:使用ZStack叠加所有通知视图,通过opacity(透明度)和offset(位移)的变化配合animation修饰符,实现切换时的淡入淡出+上滑效果,替代原来无效的.transition。
  2. Timer调整:将Timer间隔改为7.5秒,匹配需求。
  3. 进度条逻辑修复:重新实现progress()函数,基于当前通知的实际显示时长计算进度,让激活状态的气泡进度条能正确从0到1填充。
  4. 循环切换优化:用(currentIndex + 1) % notifications.count实现通知的循环切换,简化原来的判断逻辑。

如果需要其他过渡效果(如缩放、左右滑入),只需修改opacity和offset的参数,或替换为scaleEffect、offset(x:)等属性即可。

内容的提问来源于stack exchange,提问作者user20273483

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 23:25:31