首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐

  • 24-12-03 05:04
  • 3082
  • 9284
juejin.cn

在日常开发中,经常会有展示计时类的定时更新类任务,比如要展示当前时间,比较常见的方式是通过Timer:

swift
代码解读
复制代码
struct DisplayTime: View { @State private var date = Date.now @State private var timerToken: AnyCancellable? private var formatter = DateFormatter() var body: some View { List { Section("Timer") { Text(formatDate(date)) .font(.largeTitle) } } .onAppear { timerToken = Timer.publish(every: 1, on: .main, in: .common) .autoconnect() .sink { _ in date = .now } } } private func formatDate(_ date: Date) -> String { formatter.timeStyle = .medium return formatter.string(from: date) } }

在iOS 15以后,SwiftUI引入了一个新的组件叫TimelineView,可以更快捷的实现上述功能:

swift
代码解读
复制代码
TimelineView(.periodic(from: .now, by: 1)) { context in Text(formatDate(context.date)) .font(.largeTitle) }

这个组件可以让使用者省去管理Timer的工作。

TimelineView

TimelineView是一个本身没有视图的View,可以按照使用者提供的调度策略来自动刷新内容视图。

swift
代码解读
复制代码
extension TimelineView : View where Content : View { public init(_ schedule: Schedule, @ViewBuilder content: @escaping (TimelineViewDefaultContext) -> Content) }

使用TimelineView需要两部分内容:调度策略和视图的展示逻辑

调度策略

SwiftUI提供了几种可以直接使用的调度策略:

periodic
swift
代码解读
复制代码
public static func periodic(from startDate: Date, by interval: TimeInterval) -> PeriodicTimelineSchedule

periodic调度策略会按照指定的时间间隔定期执行

everyMinute
swift
代码解读
复制代码
public static var everyMinute: EveryMinuteTimelineSchedule { get }

每分钟开始的时候执行

explicit
swift
代码解读
复制代码
public static func explicit<S>(_ dates: S) -> ExplicitTimelineSchedule<S> where Self == ExplicitTimelineSchedule<S>, S : Sequence, S.Element == Date

按照指定的时间序列执行展示逻辑, 如下所示,指定了一个立即,5s,10s更新界面的策略

swift
代码解读
复制代码
TimelineView(.explicit([ .now, .now.addingTimeInterval(5), .now.addingTimeInterval(10) ])) { context in Text(formatDate(context.date)) .font(.largeTitle) }

而之前的两种可以看做是时间序列可以不断刷新的explicit的特例

animation
swift
代码解读
复制代码
public static func animation(minimumInterval: Double? = nil, paused: Bool = false) -> AnimationTimelineSchedule public static var animation: AnimationTimelineSchedule { get }

animation策略如其名字所示,是为了用于完成一些动画效果的,刷新频率和屏幕的刷新频率一致。如下所示可以创建一个简易的字母循环滚动的效果。

swift
代码解读
复制代码
GeometryReader { reader in TimelineView(.animation) { context in let _ = { dataModel.offset -= 1 if dataModel.offset <= -reader.size.width { dataModel.offset = 0 } }() Text("Hello TimelineView") .font(.largeTitle) .offset(x: dataModel.offset) } }

Sep-21-2024 15-33-53.gif

调度策略说明场景
periodic定期执行,间隔可控显示动态数据,比如倒计时
everyMinute每分钟开始时触发展示当前时间或刷新分钟级数据
explicit根据指定的时间序列触发需要在特定时刻更新视图
animation为动画效果设计,刷新的频率与屏幕刷新一致实现滚动或其他高帧率效果的动画

展示逻辑

展示逻辑接收一个context参数,context包含两个属性

swift
代码解读
复制代码
/// 触发刷新的时间点 public let date: Date /// 系统建议的刷新频率 public let cadence: TimelineView<Schedule, Content>.Context.Cadence

简易跑马灯

在前面的介绍里,实现了一个简易跑马灯,但是有两个问题:

  1. 正常的跑马灯应该是从屏幕的一侧消失的内容会从屏幕的另一侧出来
  2. 更新位移是在TimelineView的每个更新周期里固定加了1,但是可能这个更新间隔可能不能保证完全一样 所以可以对上面的代码做些修改,变成一个简易的走马灯组件:
swift
代码解读
复制代码
inal class SimpleMarqueeDataContext { var offset: CGFloat = 0 var contentWidth: CGFloat = 0 private var lastTimeInterval: TimeInterval? var containerWidth: CGFloat = 0 public func updateOffset(_ timeInterval: TimeInterval) { if let lastTimeInterval = lastTimeInterval { let diff = timeInterval - lastTimeInterval offset += diff * 50 } if offset >= containerWidth { offset = 0 } lastTimeInterval = timeInterval } } struct SimpleMarquee<Content>: View where Content: View { private var content: () -> Content private var dataContext: SimpleMarqueeDataContext = .init() init(@ViewBuilder content: @escaping () -> Content) { self.content = content } var body: some View { GeometryReader { reader in TimelineView(.animation) { context in let _ = { dataContext.updateOffset(context.date.timeIntervalSince1970) }() HStack(spacing: 0) { content() .measureWidth { width in dataContext.contentWidth = width } content() .offset(x: reader.size.width - dataContext.contentWidth) } .offset(x: -dataContext.offset) } .onAppear { dataContext.containerWidth = reader.size.width } } } }

改动的点主要是:

  1. 组件通过context上的date和上次date的差值来计算位移的增加量
  2. 为了实现从屏幕另一侧出现的效果,多增加了一个内容视图的实例 这个组件可以这么使用:
swift
代码解读
复制代码
struct SimpleMarqueeView: View { var body: some View { VStack { SimpleMarquee { VStack { Image(systemName: "globe") .imageScale(.large) .foregroundStyle(.tint) Text("Hello, TimelineView!") } .padding() .border(.red) } } } }

最终的效果:

Sep-21-2024 15-59-22.gif

Demo

注:本文转载自juejin.cn的西丘的文章"https://juejin.cn/post/7416605082689437746"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2492) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

141
iOS
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2025 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top