这是我第一次使用原生的Swift Charts框架,我在图表的x轴上遇到了一个问题。
首先,让我告诉你我想要实现的图表行为:
我想要渲染一个图表,显示最近七天(包括今天)的体重数据,类似于苹果健康应用程序在“周”选项卡中的显示方式。从苹果健康的截图中可以看到,x轴始终显示最近七天的日期,无论实际上是否有对应日期的数据。在左侧的截图中,你可以看到一个完整的星期,其中所有日期都有有效数据。在右侧的截图中,你可以看到一个完整的星期,但是最后一天(“今天”)的数据被删除了。苹果健康仍然正确地显示该日期在x轴上,只是不使用该数据点来绘制线状图,保持x轴固定为7天:
苹果健康应用程序中期望的x轴行为: 这是我尝试重新创建所描述的图表行为:
每个图表数据元素都有一个.date和可选的.averageWeight属性:
我使用一个包含7个
需要注意的是,数组中可能包含
这是我尝试绘制图表的方式(
我使用这两个修饰符来确保我的y轴刻度围绕数据数组的最小值和最大值.averageWeight。
这个(γ轴自定义)按预期工作。如果您认为这些可能导致我的x轴相关问题,还有`getBottomRange()`、`getTopRange()`和`getYMarkCount()`。
对于x轴,我使用这个方法为每一天创建一个
让我现在向您展示一下,如果有一天的.averageWeight == nil会发生什么情况:
这并不太令人惊讶,因为当第一个或最后一个元素被跳过时,图表甚至不知道它必须显示7个日期。但是我仍然希望没有
如何实现所描述的图表行为,即在可能为
感谢您阅读所有内容并尝试帮助我。
首先,让我告诉你我想要实现的图表行为:
我想要渲染一个图表,显示最近七天(包括今天)的体重数据,类似于苹果健康应用程序在“周”选项卡中的显示方式。从苹果健康的截图中可以看到,x轴始终显示最近七天的日期,无论实际上是否有对应日期的数据。在左侧的截图中,你可以看到一个完整的星期,其中所有日期都有有效数据。在右侧的截图中,你可以看到一个完整的星期,但是最后一天(“今天”)的数据被删除了。苹果健康仍然正确地显示该日期在x轴上,只是不使用该数据点来绘制线状图,保持x轴固定为7天:
苹果健康应用程序中期望的x轴行为: 这是我尝试重新创建所描述的图表行为:
每个图表数据元素都有一个.date和可选的.averageWeight属性:
struct WeightChartData: Identifiable
{
var id = UUID()
var date: Date
var averageWeight: Double?
}
我使用一个包含7个
WeightChartData
元素的数据数组,表示用户过去7天的体重数据(包括今天)。任何一天的体重数据可能为nill
(当该天没有检索到HealthKit数据时)。数组被填充了数据,以便最后一个元素代表"今天"。以下是我的测试/调试测试数组(这样你可以更好地看到我的实际数据数组的结构):var testData: [WeightChartData] =
[
WeightChartData(date: Calendar.current.date(byAdding: .day, value: -6, to: Calendar.current.startOfDay(for: Date()))!, averageWeight: 95.1),
WeightChartData(date: Calendar.current.date(byAdding: .day, value: -5, to: Calendar.current.startOfDay(for: Date()))!, averageWeight: 94.2),
WeightChartData(date: Calendar.current.date(byAdding: .day, value: -4, to: Calendar.current.startOfDay(for: Date()))!, averageWeight: 94.4),
WeightChartData(date: Calendar.current.date(byAdding: .day, value: -3, to: Calendar.current.startOfDay(for: Date()))!, averageWeight: nil),
WeightChartData(date: Calendar.current.date(byAdding: .day, value: -2, to: Calendar.current.startOfDay(for: Date()))!, averageWeight: 93.5),
WeightChartData(date: Calendar.current.date(byAdding: .day, value: -1, to: Calendar.current.startOfDay(for: Date()))!, averageWeight: 93.6),
WeightChartData(date: Calendar.current.startOfDay(for: Date()), averageWeight: nil)
]
需要注意的是,数组中可能包含
WeightChartData
元素,其中.averageWeight == nill
,表示这些天没有体重数据。
这是我尝试绘制图表的方式(
WeightViewModel.Data
是我的“生产”数据数组,包含7个 WeightChartData
元素)。请注意条件语句 LineMark(...)
。Chart(WeightViewModel.Data)
{ item in
if item.averageWeight != nil
{
LineMark(
x: .value("Date", item.date, unit: .weekday),
y: .value("Value", item.averageWeight!)
)
}
}
我使用这两个修饰符来确保我的y轴刻度围绕数据数组的最小值和最大值.averageWeight。
.chartYScale(
domain: [ getBottomRange(), getTopRange() ]
)
.chartYAxis
{
AxisMarks(
position: .leading,
values: .automatic(desiredCount: getYMarkCount())
)
}
这个(γ轴自定义)按预期工作。如果您认为这些可能导致我的x轴相关问题,还有`getBottomRange()`、`getTopRange()`和`getYMarkCount()`。
func getTopRange() -> Int
{
return Int(round(WeightViewModel.maxWeightData()?.averageWeight ?? 0.0) + 1.0)
}
func getBottomRange() -> Int
{
return Int(round(WeightViewModel.minWeightData()?.averageWeight ?? 0.0) - 1.0)
}
func getYMarkCount() -> Int
{
return abs(getTopRange() - getBottomRange())
}
对于x轴,我使用这个方法为每一天创建一个
AxisMark(...)
(所以x轴上有7个元素,不管包含重量数据的元素还是nil,就像在Apple Health中一样)。.chartXAxis
{
AxisMarks(values: .stride(by: .day))
{ date in
AxisGridLine()
AxisValueLabel(format: .dateTime.weekday(.narrow), centered: true)
}
}
让我现在向您展示一下,如果有一天的.averageWeight == nil会发生什么情况:
- 如果WeightViewModel.Data的所有元素都有有效的.averageWeight:图表将正确绘制(即在x轴上绘制所有7天):
.averageWeight == nil
,图表会正确地跳过这个y值,同时仍然在x轴上显示该天:
就像在Apple Health中一样。在这个例子中,只有倒数第二个元素的.averageWeight == nil
。
- 问题是当第一个或最后一个元素的
.averageWeight == nil
时。同样,因为LineMark(..)
只有在item.averageWeight != nil
时才会被调用,所以这个第一个或最后一个数据点会被跳过。但这一次,由于它是第一个或最后一个元素,我的x轴刻度会被打乱。它不再显示7天,而是从图表中删除没有weightData的那一天,结果只剩下6天的x轴:
.averageWeight
设置为nil
。
请注意,星期四不再显示在x轴上。这并不太令人惊讶,因为当第一个或最后一个元素被跳过时,图表甚至不知道它必须显示7个日期。但是我仍然希望没有
.averageWeight
的那一天显示在图表的x轴上(因为我的x轴应该始终有7个元素代表最近7天)。如何实现所描述的图表行为,即在可能为
nil
的可选数据存在时始终显示完整的一周?感谢您阅读所有内容并尝试帮助我。