苹果橙子我最爱 發表於 2022-10-27 10:39:23

Swift 图表使用Foudation库中测量类型详解

<div id="navCategory"><h5 class="catalogue">目录</h5><ul class="first_class_ul"><li>前言</li><li>定义图表的数据</li><li>在图表中使用测量值</li><li>设计一个包装器类型</li><li>显示格式化标签</li></ul></div><p class="maodian"></p><h2>前言</h2>
<p>在这篇文章中,我们将建立一个条形图,比较基督城地区自然散步的持续时间。我们将使用今年推出的新的Swift <code>Charts</code> 框架,并将看到如何绘制默认不符合 <code>Plottable</code> 协议的类型的数据,如 <code>Measurement&lt;UnitDuration&gt;</code>。</p>
<p class="maodian"></p><h2>定义图表的数据</h2>
<p>让我们先定义一下要在图表中展现的数据。</p>
<p>我们声明了一个包含标题和步行时间(小时)的 <code>Walk</code> 结构体。我们使用 <code>Foundation</code> 框架中的测量类型Measurement和单位类型UnitDuration来表示每次步行的时间。</p>
<div class="jb51code"><pre class="brush:cpp;">struct Walk {
    let title: String
    let duration: Measurement&lt;UnitDuration&gt;
}
</pre></div>
<p>我们在数组 <code>works</code> 中存储要在图表中显示的数据。</p>
<div class="jb51code"><pre class="brush:cpp;">let walks = [
    Walk(
      title: "Taylors Mistake to Sumner Beach Coastal Walk",
      duration: Measurement(value: 3.1, unit: .hours)
    ),
    Walk(
      title: "Bottle Lake Forest",
      duration: Measurement(value: 2, unit: .hours)
    ),
    Walk(
      title: "Old Halswell Quarry Loop",
      duration: Measurement(value: 0.5, unit: .hours)
    ),
    ...
]
</pre></div>
<p class="maodian"></p><h2>在图表中使用测量值</h2>
<p>尝试直接在图表中使用测量值</p>
<p>让我们定义一个 <code>Chart</code>,并将 <code>walks</code> 数组作为数据参数传递给它。因为我们知道我们的<code>walk</code> 标题是唯一的,所以我们可以直接使用它们作为 <code>id</code>,但你也可以将你的数据模型改为 <code>Identifiable</code>。</p>
<div class="jb51code"><pre class="brush:cpp;">Chart(walks, id: \.title) { walk in
    BarMark(
      x: .value("Duration", walk.duration),
      y: .value("Walk", walk.title)
    )
}
</pre></div>
<p>注意,因为 <code>Measurement&lt;UnitDuration&gt;</code> 没有遵守 <code>Plottable</code> 协议,我们会得到一个错误:「Initializer &#39;init(x:y:width:height:stacking:)&#39; requires that &#39;Measurement&#39; conform to &#39;Plottable&#39;」</p>
<p><code>BarkMark</code> 的初始化器期望收到一个用于 x 和 y 的 <code>PlottableValue</code> 参数。而且 <code>PlottableValue</code> 的值类型必须符合 <code>Plottable</code> 协议。</p>
<p>我们有几个选择来解决这个错误。我们可以提取测量值的 <code>value</code>,它是一个 <code>Double</code> 类型,它是默认符合 <code>Plottable</code> 的,我们可以扩展具有 <code>Plottable</code> 一致性的 <code>Measurement&lt;UnitDuration&gt;</code>,或者我们可以定义一个包装了测量的类型并使其符合 <code>Plottable</code> 协议。</p>
<p>如果我们简单地从测量值中提取,我们就会失去上下文,不知道用什么单位来创建测量值。这意味着,我们将无法正确格式化图表的标签来向用户表示单位。虽然我们可以记住我们在创建测量时使用了小时 <code>hours</code>,但这并不理想。例如,我们可以决定以后改变数据模型,以分钟为单位存储持续时间,或者数据可能来自其他地方,所以手动重构单位并不是一个完美的解决方案。</p>
<p>用 <code>Plottable</code> 的一致性来扩展 <code>Measurement&lt;UnitDuration&gt;</code> 是可行的,但根据 Swift 中关于外部类型的追溯一致性的警告 (Warning for Retroactive Conformances of External Types),如果 Swift Charts 在未来添加了这种一致性,它可能会被破坏。</p>
<p>我们将研究如何定义我们自己的类型来包装 <code>measurement</code>,并为我们的自定义类型添加 <code>Plottable</code> 的一致性。</p>
<p class="maodian"></p><h2>设计一个包装器类型</h2>
<p>设计一个符合 Plottable 标准的包装器类型</p>
<p>我们将定义一个自定义的 <code>PlottableMeasurement</code> 类型,并使其成为通用的,所以它可以容纳任何类型的单位的测量类型。</p>
<div class="jb51code"><pre class="brush:cpp;">struct PlottableMeasurement&lt;UnitType: Unit&gt; {
    var measurement: Measurement&lt;UnitType&gt;
}
</pre></div>
<p>然后,我们将为 <code>PlottableMeasurement</code> 添加 <code>Plottable</code> 的一致性,其单位为 <code>UnitDuration</code> 类型。我们可以在将来添加对其他单位的支持。</p>
<div class="jb51code"><pre class="brush:cpp;">extension PlottableMeasurement: Plottable where UnitType == UnitDuration {
    var primitivePlottable: Double {
      self.measurement.converted(to: .minutes).value
    }
    init?(primitivePlottable: Double) {
      self.init(
            measurement: Measurement(
                value: primitivePlottable,
                unit: .minutes
            )
      )
    }
}
</pre></div>
<p><code>Plottable</code> 协议有两个要求: <code>primitivePlottable</code> 属性必须返回原始类型之一,如 <code>Double</code>、<code>String</code> 或 <code>Date</code>,以及一个可失败的初始化器,从原始 <code>plottable</code> 类型创建一个值。</p>
<p>我决定将测量值转换为分钟,但你可以选择适合你需要的任何其他单位。只是在与原始值转换时要使用相同的单位,这一点很重要。</p>
<p>我们现在可以更新我们的图表,以使用我们的自定义 <code>Plottable</code> 类型。</p>
<div class="jb51code"><pre class="brush:cpp;">Chart(walks, id: \.title) { walk in
    BarMark(
      x: .value(
            "Duration",
            PlottableMeasurement(measurement: walk.duration)
      ),
      y: .value("Walk", walk.title)
    )
}
</pre></div>
<p>它可以工作,但X轴上的标签没有格式化,没有向用户显示测量单位。我们接下来要解决这个问题。</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202210/20221027100606013.jpg" /></p>
<p class="maodian"></p><h2>显示格式化标签</h2>
<p>显示带有测量单位的格式化标签</p>
<p>为了定制X轴上的标签,我们将使用<code>chartXAxis(content:)</code>修改器,并用传递给我们的值重构x轴的标记。</p>
<div class="jb51code"><pre class="brush:cpp;">Chart(walks, id: \.title) { ... }
    .chartXAxis {
      AxisMarks { value in
            AxisGridLine()
            AxisValueLabel("""
            \(value.as(PlottableMeasurement.self)!
                .measurement
                .converted(to: .hours),
            format: .measurement(
                width: .narrow,
                numberFormatStyle: .number.precision(
                  .fractionLength(0))
                )
            )
            """)
      }
    }
</pre></div>
<p>我们首先添加网格线,然后重构给定值的标签。</p>
<p><code>AxisValueLabel</code>在初始化器中接受一个<code>LocalizedStringKey</code>,它可以通过插值测量和指定其格式风格来构建。</p>
<p>我们收到的值是使用我们在 <code>Plottable</code> 一致性中定义的初始化器创建的,所以在我们的案例中,测量值是以分钟为单位提供的。但我相信对于这个特定的图表,使用小时会更好。我们可以很容易地将测量值转换为插值内部所需的单位。在这里,我们确定该值是 <code>PlottableMeasurement</code> 类型的,所以我们可以强制解包类型转换。</p>
<p>我选择了缩小的格式和小数点后零位数作为数字样式,但你可以根据你的具体图表调整这些设置。</p>
<p>最后的结果是在X轴上显示以小时为单位的格式化持续时间。</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202210/20221027100606014.jpg" /></p>
<p>你可以从我们的 GitHub repo 中获得这篇文章中使用的项目的完整 示例代码。</p>
<p>以上就是Swift 图表使用Foudation库中测量类型详解的详细内容,更多关于Swift 图表Foudation库测量类型的资料请关注琼殿技术社区其它相关文章!</p>
                           
                            <div class="art_xg">
                              <b>您可能感兴趣的文章:</b><ul><li>swift内存管理指针类型使用实例详解</li><li>Swift enum枚举类型使用详解</li><li>详细讲解Swift中的类型占位符</li><li>Swift 基本数据类型详解总结</li><li>Swift 字符串类型及常用方法详解总结</li><li>Swift设计思想Result&lt;T&gt;与Result&lt;T, E: Error&gt;类型解析</li></ul>
                            </div>

                        </div>
                        <!--endmain-->
頁: [1]
查看完整版本: Swift 图表使用Foudation库中测量类型详解