除了 WorkoutManager 类中的一些细微变化外,我几乎完全遵循this Apple Sample Code。但是,在 Always On 模式下的模拟器上进行测试时,Apple 的应用程序将其
context.cadence
从 .live
切换为 .seconds
,而我的应用程序将其 context.cadence
切换为 .minutes
。因此,我的应用程序没有像 Apple 应用程序那样经常更新。我不知道我在哪里做任何不同的事情或如何将我的设置为.seconds
- 我认为这是默认设置?
我的代码:
struct LiveWorkoutTabView: View {
@State var tabIndex:Int = 1
@EnvironmentObject var workoutManager:
var body: some View {
if workoutManager.workoutIsActive {
TimelineView(MetricsTimelineSchedule(from: workoutManager.startDate ?? Date(),
isPaused: workoutManager.session?.state == .paused)) { context in
TabView(selection: $tabIndex) {
WorkoutControlsView(liveWorkoutTabViewIndex: $tabIndex).environmentObject(workoutManager)
.tabItem { Group{
}}.tag(0)
LiveWorkoutHeartView().environmentObject(workoutManager)
.tabItem { Group{
}}.tag(1)
}
.onChange(of: context.cadence, perform: { newValue in
print("cadence = \(newValue)")
})
.navigationTitle {
HStack {
ElapsedTimeView(elapsedTime: workoutManager.builder?.elapsedTime(at: context.date) ?? 0, showSubseconds: context.cadence == .live)
Spacer()
}
}
}
}
}
}
struct WorkoutTabView_Previews: PreviewProvider {
static var previews: some View {
LiveWorkoutTabView().environmentObject(WorkoutManager())
}
}
//For making the timer only show seconds when Always On display is on, Code from: https://developer.apple.com/documentation/healthkit/workouts_and_activity_rings/build_a_workout_app_for_apple_watch
struct ElapsedTimeView: View {
var elapsedTime: TimeInterval = 0
var showSubseconds: Bool = true
@State private var timeFormatter = ElapsedTimeFormatter()
var body: some View {
Text(NSNumber(value: elapsedTime), formatter: timeFormatter)
.font(.body)
.foregroundColor(.white)
.monospacedDigit()
.onChange(of: showSubseconds) {
timeFormatter.showSubseconds = $0
}
}
}
class ElapsedTimeFormatter: Formatter {
let componentsFormatter: DateComponentsFormatter = {
let formatter = DateComponentsFormatter()
formatter.allowedUnits = [.minute, .second]
formatter.zeroFormattingBehavior = .pad
return formatter
}()
var showSubseconds = true
override func string(for value: Any?) -> String? {
guard let time = value as? TimeInterval else {
return nil
}
guard let formattedString = componentsFormatter.string(from: time) else {
return nil
}
if showSubseconds {
let hundredths = Int((time.truncatingRemainder(dividingBy: 1)) * 100)
let decimalSeparator = Locale.current.decimalSeparator ?? "."
return String(format: "%@%@%0.2d", formattedString, decimalSeparator, hundredths)
}
return formattedString
}
}
private struct MetricsTimelineSchedule: TimelineSchedule {
var startDate: Date
var isPaused: Bool
init(from startDate: Date, isPaused: Bool) {
self.startDate = startDate
self.isPaused = isPaused
}
func entries(from startDate: Date, mode: TimelineScheduleMode) -> AnyIterator<Date> {
var baseSchedule = PeriodicTimelineSchedule(from: self.startDate,
by: (mode == .lowFrequency ? 1.0 : 1.0 / 30.0))
.entries(from: startDate, mode: mode)
return AnyIterator<Date> {
guard !isPaused else { return nil }
return baseSchedule.next()
}
}
}
苹果代码:
struct MetricsView: View {
@EnvironmentObject var workoutManager: WorkoutManager
var body: some View {
TimelineView(MetricsTimelineSchedule(from: workoutManager.builder?.startDate ?? Date(),
isPaused: workoutManager.session?.state == .paused)) { context in
VStack(alignment: .leading) {
ElapsedTimeView(elapsedTime: workoutManager.builder?.elapsedTime(at: context.date) ?? 0, showSubseconds: context.cadence == .live)
.foregroundStyle(.yellow)
Text(Measurement(value: workoutManager.activeEnergy, unit: UnitEnergy.kilocalories)
.formatted(.measurement(width: .abbreviated, usage: .workout, numberFormatStyle: .number.precision(.fractionLength(0)))))
Text(workoutManager.heartRate.formatted(.number.precision(.fractionLength(0))) + " bpm")
Text(Measurement(value: workoutManager.distance, unit: UnitLength.meters).formatted(.measurement(width: .abbreviated, usage: .road)))
}
.font(.system(.title, design: .rounded).monospacedDigit().lowercaseSmallCaps())
.frame(maxWidth: .infinity, alignment: .leading)
.ignoresSafeArea(edges: .bottom)
.scenePadding()
.onChange(of: context.cadence, perform: { newValue in
print("cadence = \(newValue)")
})
}
}
}
struct MetricsView_Previews: PreviewProvider {
static var previews: some View {
MetricsView().environmentObject(WorkoutManager())
}
}
private struct MetricsTimelineSchedule: TimelineSchedule {
var startDate: Date
var isPaused: Bool
init(from startDate: Date, isPaused: Bool) {
self.startDate = startDate
self.isPaused = isPaused
}
func entries(from startDate: Date, mode: TimelineScheduleMode) -> AnyIterator<Date> {
var baseSchedule = PeriodicTimelineSchedule(from: self.startDate,
by: (mode == .lowFrequency ? 1.0 : 1.0 / 30.0))
.entries(from: startDate, mode: mode)
return AnyIterator<Date> {
guard !isPaused else { return nil }
return baseSchedule.next()
}
}
}