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

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:您能够提供更新的开端时刻和频率。

尽管您能够经过这种方式创立时刻线:

Timeline(EveryMinuteTimelineSchedule()) { timeline in
    ...
}

自 Swift 5.5 和SE-0299的引入以来,咱们现在支持相似枚举的语法。这使代码更具可读性并改进了主动完结功用。主张咱们运用以下语法:

TimelineView(.everyMinute) { timeline in
    ...
}

注:你可能听说过,但今年也推出了款式。更好的是,关于款式,只需您运用 Swift 5.5,您就能够将其与曾经的版别一起反向布置。

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

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

您甚至能够创立自己的(不要忘记 static 关键字!):

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并将接收一组时刻偏移量。每个偏移值将相关于数组中的前一个值。当调度程序耗尽偏移量时,它将循环回到数组的最初并重新开端。

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()函数来生成时刻线中的日期。咱们的调度程序会记住最终一个日期并添加适当的偏移量。当没有更多偏移量时,它循环回到数组中的第一个。

最终,为咱们的调度程序如虎添翼的是创立一个相似枚举的初始值设定项:

extension TimelineSchedule where Self == CyclicTimelineSchedule {
    static func cyclic(timeOffsets: [TimeInterval]) -> CyclicTimelineSchedule {
            .init(timeOffsets: timeOffsets)
    }
}