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

SwiftUI定时器运行时如何实现气泡从灰色到白色的渐变填充?

问题解决:通知横幅气泡随时间渐变填充效果

问题分析

当前代码的核心问题是填充进度基于通知索引计算,而非当前通知的实际显示时长。这导致切换通知时填充进度直接跳变,无法实现“7.5秒内从灰色缓慢填充为白色”的动态效果。需要引入实时时长跟踪,让填充进度随时间平滑变化。

修改后的完整代码

struct SoleInAppNotifications: View {
    @State private var currentIndex: Int = 0
    @State private var isPaused: Bool = false
    @State var hasImage: Bool? = true
    // 跟踪当前通知已显示的时长
    @State private var elapsedTime: TimeInterval = 0
    
    // 主定时器:7.5秒切换通知
    private let notificationTimer = Timer.publish(every: 7.5, on: .main, in: .common).autoconnect()
    // 进度更新定时器:每0.05秒更新一次填充进度,保证动画平滑
    private let progressTimer = Timer.publish(every: 0.05, on: .main, in: .common).autoconnect()

    var body: some View {
        HStack(spacing: .zero) {
            if hasImage ?? false {
                Image("raiff")
                    .padding(.trailing, 17)
            }
            VStack(alignment: .leading, spacing: .zero) {
                Text("Notification #\(currentIndex + 1)")
                    .foregroundColor(.white)
                    .lineLimit(1)
                    .padding(.bottom, 1)
                Text("Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, se")
                    .foregroundColor(.white)
                    .lineLimit(2)
            }
            Spacer() // 添加 spacer 把气泡推到右侧
            VStack(spacing: 8) { // 调整气泡间距,让布局更合理
                ForEach(0..<5, id: \.self) { index in
                    BubbleView(
                        isActive: index == currentIndex,
                        progress: calculateProgress()
                    )
                }
            }
        }
        .frame(maxWidth: .infinity)
        .padding(17)
        .background(Color.black)
        .onReceive(notificationTimer) { _ in
            guard !isPaused, currentIndex < 4 else { // 修正边界:5条通知索引0-4
                return
            }
            withAnimation {
                currentIndex += 1
                elapsedTime = 0 // 切换通知时重置时长
            }
        }
        .onReceive(progressTimer) { _ in
            guard !isPaused, currentIndex < 5 else {
                return
            }
            // 限制时长不超过7.5秒
            elapsedTime = min(elapsedTime + 0.05, 7.5)
        }
    }
    
    // 计算当前通知的填充进度(0到1)
    private func calculateProgress() -> CGFloat {
        CGFloat(elapsedTime / 7.5)
    }
}

struct BubbleView: View {
    let isActive: Bool
    let progress: CGFloat // 接收实时进度值(0-1)

    var body: some View {
        ZStack {
            if isActive {
                // 灰色背景条
                RoundedRectangle(cornerRadius: 3)
                    .fill(Color.gray)
                    .frame(width: 6, height: 24)
                // 白色填充层,随进度变化
                RoundedRectangle(cornerRadius: 3)
                    .fill(Color.white)
                    .frame(width: 6, height: 24 * progress)
                    .alignmentGuide(.bottom) { $0[.bottom] } // 从底部开始填充
                    .animation(.linear(duration: 0.05), value: progress) // 平滑过渡
            } else {
                // 非活跃状态的灰色小圆点
                Circle()
                    .fill(Color.gray)
                    .frame(width: 6, height: 6)
            }
        }
    }
}

关键改动说明

  1. 新增时长跟踪:添加elapsedTime状态记录当前通知已显示的时间,通过progressTimer每0.05秒更新一次,保证填充动画的平滑度。
  2. 进度计算逻辑:用elapsedTime / 7.5得到0到1之间的进度值,直接对应7.5秒的填充周期。
  3. BubbleView重构:移除依赖索引的逻辑,直接接收实时进度值,通过调整白色填充层的高度实现从底部到顶部的填充效果,使用线性动画保证匀速填充。
  4. 边界修正:调整通知切换的边界条件(currentIndex < 4),避免索引越界(5条通知索引为0-4)。
  5. 布局优化:添加Spacer将气泡栏推到右侧,调整气泡间距提升视觉效果。

内容的提问来源于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 22:52:31