SwiftUI 动画 -- TimelineView

TimelineView 是一个容器视图,它按照关联调度程序确定的频率重新绘制其内容

less 复制代码
TimelineView(.periodic(from: .now, by: 1.0)) { timeline in    
    Text("(timeline.date)")
}

在许多情况下,我们希望每次时间线更新我们的视图时我们的视图都执行一些操作。放置此代码的最佳位置是onChange(of:perform)闭包。

注意一点:时间线可能会产生一定数量的更新,但视图的内容很可能会更新更多次。

时间线调度程序

正如我们已经看到的, TimelineView需要TimelineScheduler来确定何时更新其内容。SwiftUI 提供了一些预定义的调度程序,就像我们使用的那样。但是,我们也可以创建自己的自定义调度程序。我将在下一节中详细阐述这一点。但让我们从现有的开始。

时间线调度程序基本上是采用该协议的结构TimelineScheduler。现有的类型有:

  • AnimationTimelineSchedule:尽可能快地更新,让您有机会绘制动画的每一帧。它具有可让您限制更新频率和暂停更新的参数。TimelineView与新视图结合时,这一点将非常有用Canvas
  • EveryMinuteTimelineSchedule:顾名思义,它每分钟更新一次。
  • ExplicitTimelineSchedule:您可以提供一个数组,其中包含您希望时间线更新的所有时间。
  • PeriodicTimelineSchedule:您可以提供更新的开始时间和频率。

尽管您可以通过这种方式创建时间线:

scss 复制代码
Timeline(EveryMinuteTimelineSchedule()) { timeline in
    ...
}

自 Swift 5.5 和SE-0299的引入以来,我们现在支持类似枚举的语法。这使代码更具可读性并改进了自动完成功能。建议我们使用以下语法:

scss 复制代码
TimelineView(.everyMinute) { timeline in
    ...
}

注:你可能听说过,但今年也推出了款式。更好的是,对于样式,只要您使用 Swift 5.5,您就可以将其与以前的版本一起反向部署。

对于每个现有的调度程序,可能有多个类似枚举的选项。例如,这两行创建一个 AnimationTimelineSchedule 类型的调度程序:

scss 复制代码
TimelineView(.animation) { ... }

TimelineView(.animation(minimumInterval: 0.3, paused: false)) { ... }

您甚至可以创建自己的(不要忘记 static 关键字!):

csharp 复制代码
extension TimelineSchedule where Self == PeriodicTimelineSchedule {
    static var everyFiveSeconds: PeriodicTimelineSchedule {
        get { .init(from: .now, by: 5.0) }
    }
}

struct ContentView: View {
    var body: some View {
        TimelineView(.everyFiveSeconds) { timeline in
            ...
        }
    }
}

自定义时间线调度器

我们可以创建一个HeartTimelineSchedule完全按照心脏跳动的频率需要进行更新的系统。但以可重用性的名义,让我们做一些更通用的事情,以便将来可以重用。 我们的新调度程序将被调用:CyclicTimelineSchedule 并将接收一组时间偏移量。每个偏移值将相对于数组中的前一个值。当调度程序耗尽偏移量时,它将循环回到数组的开头并重新开始。

swift 复制代码
struct CyclicTimelineSchedule: TimelineSchedule {
    let timeOffsets: [TimeInterval]
    
    func entries(from startDate: Date, mode: TimelineScheduleMode) -> Entries {
        Entries(last: startDate, offsets: timeOffsets)
    }
    
    struct Entries: Sequence, IteratorProtocol {
        var last: Date
        let offsets: [TimeInterval]
        
        var idx: Int = -1
        
        mutating func next() -> Date? {
            idx = (idx + 1) % offsets.count
            
            last = last.addingTimeInterval(offsets[idx])
            
            return last
        }
    }
}

实现 TimelineSchedule 有几个要求:

  • 提供entries(from:mode:)功能。
  • 我们的Entries类型必须符合SequencewhereEntries.Element == Date

您可以通过多种方式遵守Sequence。此示例实现并声明与和IteratorProtocol 的一致性。您可以在此处阅读有关序列一致性的更多信息。Sequence``IteratorProtocol

为了Entries实现IteratorProtocol,我们必须编写next()函数来生成时间线中的日期。我们的调度程序会记住最后一个日期并添加适当的偏移量。当没有更多偏移量时,它循环回到数组中的第一个。

最后,为我们的调度程序锦上添花的是创建一个类似枚举的初始值设定项:

swift 复制代码
extension TimelineSchedule where Self == CyclicTimelineSchedule {
    static func cyclic(timeOffsets: [TimeInterval]) -> CyclicTimelineSchedule {
            .init(timeOffsets: timeOffsets)
    }
}
相关推荐
符哥200813 小时前
一套基于Swift+MVVM为基础的iOS App 开发框架
swift
符哥200819 小时前
Swift 开发 iOS App 过程中写自定义控件的归纳总结
ios·cocoa·swift
锐意无限1 天前
Swift 扩展归纳--- UIView
开发语言·ios·swift
文件夹__iOS2 天前
AsyncStream 进阶实战:SwiftUI 全局消息流极简实现
ios·swiftui·swift
fendoudexiaoniao_ios5 天前
iOS 列表拖拽cell排序
ios·swift
CYpdpjRnUE5 天前
光伏电池PV建模及其基于Boost Buck电路的最大功率追踪MPPT算法研究及仿真效果探究
swiftui
大熊猫侯佩6 天前
Swift 6 驱魔实录:揭开 Combine 与 @Sendable 的“血色契约”
swift·block·combine·preconcurrency·sendable·mainactor·isolation
初级代码游戏6 天前
iOS开发 SwiftUI 15:手势 拖动 缩放 旋转
ios·swiftui·swift
ujainu6 天前
Flutter + OpenHarmony 游戏开发进阶:虚拟摄像机系统——平滑跟随与坐标偏移
开发语言·flutter·游戏·swift·openharmony
zhyongrui8 天前
SnipTrip 菜单 Liquid Glass 实现方案:结构、材质、交互与深浅色策略
ios·性能优化·swiftui·交互·开源软件·材质