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

