概览 WWDC 2023 为我们带来了 iOS 17,也为我们带来了 SwiftUI 5.0。 在 SwiftUI 新版中,Apple 增加了很多重磅功能,也对原有功能做了大幅度升级。 对于 Cha
WWDC 2023 为我们带来了 iOS 17,也为我们带来了 SwiftUI 5.0。
在 SwiftUI 新版中,Apple 增加了很多重磅功能,也对原有功能做了大幅度升级。
对于 Charts 框架, 新增了饼图(Pie)类型并且加入了图表元素的原生选择功能。
在本篇博文中,就让我们一起来看看 SwiftUI 5.0 中这些激动人心的新功能吧!
Let’s Go!!!😃
SwiftUI 5.0 在 4.0 众多图表类型基础之上,增加了全新的 饼图(Pie) 类型,我们可以通过它来更形象的展示图表数据。
注意:本文中的代码需要 Xcode 15 beta 版才能编译和运行。
下面是 SwiftUI 4.0 Charts 条状图的展示:
代码如下:
@Modelfinal class Item { var name: String var power: Int var timestamp: Date init(name: String, power: Int) { self.name = name self.power = power timestamp = Date.now }}Chart(items) { item in BarMark(x: .value("power", item.power), stacking: .nORMalized) .foregroundStyle(by: .value("name", item.name))}.chartLegend(.hidden)
想改为使用新饼图类型非常简单,只需将上面的 BarMark 换为 SectorMark 即可:
SectorMark(angle: .value("power", item.power))
我们可以调整每块“大饼”的空隙大小(angularInset)和圆角的弧度(cornerRadius):
SectorMark(angle: .value("power", item.power),angularInset: 3.0) .cornerRadius(10)
值得注意的是:Charts 中饼图数据改变的动画效果做的也非常生动,SwiftUI 会自动根据状态的变化来合成自然的动画,无需多写半行代码。
不过,“大饼”虽好,“甜甜圈”更佳!
小孩子才做选择,光有“大饼”怎么行,我们连“甜甜圈”也统统都要了🍩!
实现“甜甜圈”(饼图空心)效果也很容易,我们只需调整 SectorMark 构造器中 innerRadius 属性的值即可:
SectorMark(angle: .value("power", item.power), innerRadius: .ratio(innerRadius), angularInset: 3.0)
好诱人的“甜甜圈”哦,有没有想吃的欲望呢?😉
除了加入新图表类型以外,SwiftUI 5.0 中 Charts 终于可以支持原生选择啦!
现在,我们无需再手动计算是图表中哪个元素被选中了,一切回归简洁:
struct LocationDetailsChart: View { @Binding var rawSelectedDate: Date? var body: some View { Chart { ForEach(data) { series in ForEach(series.sales, id: \.day) { element in LineMark( x: .value("Day", element.day, unit: .day), y: .value("Sales", element.sales) ) } .foregroundStyle(by: .value("City", series.city)) .symbol(by: .value("City", series.city)) .interpolationMethod(.catmullRom) } } .chartXSelection(value: $rawSelectedDate) }}
如上代码所示,我们使用 chartXSelection(value:) 修改器方法将当前选中的数据放入指定的绑定($rawSelectedDate)中。
除了选择单个图表元素,我们还可以选择一段范围内的元素集合:
Chart(data) { series in ForEach(series.sales, id: \.day) { element in LineMark( x: .value("Day", element.day, unit: .day), y: .value("Sales", element.sales) ) } ...}.chartXSelection(value: $rawSelectedDate).chartXSelection(range: $rawSelectedRange)
那么问题来了,能不能选中 SwiftUI 5.0 图表新饼图类型的“大饼”元素呢?答案是肯定的!
下面是官方视频中对应的代码:
Chart(data, id: \.name) { element in SectorMark( angle: .value("Sales", element.sales), innerRadius: .ratio(0.618), angularInset: 1.5 ) .cornerRadius(5) .foregroundStyle(by: .value("Name", element.name)) .opacity(element.name == selectedName ? 1.0 : 0.3)}.chartAngleSelection(value: $selectedAngle)
类似的, 通过 chartAngleSelection(value:) 修改器方法实现了饼图元素的选中:
不过,单从这段代码我们还是无法了解饼图元素选中的实现细节,比如:selectedAngle 是什么?它是如何转换成 selectedName 的呢?
为什么 在此要“犹抱琵琶半遮面”隐藏相关的细节呢?这不禁让我预感到它会是一个“坑”!
“坑”中的实现很可能在 iOS 17 正式版中会有所不同,所以 才会这样“遮遮掩掩”。
想要了解更多相关的内容,请移步如下链接观赏:
WWDC 23 中对应内容的官方视频在下面,想要了解来龙去脉的小伙伴们可以“肆意”观赏:
尽管官方视频中的代码对如何完成饼图元素选中功能“闪烁其词”,但我们可以自己发挥“主观能动性”来大胆推测一下它的实现细节:即自己搞定“甜甜圈”的选中功能。
首先我们要搞清楚的是, chartAngleSelection 方法参数中的绑定值到底是个啥:
public func chartAngleSelection<P>(_ binding: Binding<P?>) -> some View where P : Plottable
我们可以通过监视 angleValue 的值,来看看它是如何跟随我们点击而变化的:
struct ContentView: View { // 省略其它状态定义... @Query private var items: [Item] @State private var angleValue: Int? var body: some View { NavigationView { List { Chart(items) { item in SectorMark(angle: .value("power", item.power), innerRadius: .ratio(innerRadius), angularInset: 3.0 ) .cornerRadius(10) .foregroundStyle(by: .value("name", item.name)) } .chartLegend(.hidden) .chartAngleSelection($angleValue) .onChange(of: angleValue){ old,new in // 探查 angleValue 的真正面目... print("new angle value: \(new)") }.padding(.vertical, 50) ForEach(items) { ... } } .navigationTitle("饼图演示") } }}
如上图所示:chartAngleSelection($angleValue) 方法中的绑定是一个数量值(定义成浮点数类型也可以),我们还发现 angleValue 在 0° 位置附近点击时值越小,而在 360° 位置点击时值越大。
经过验证可得:angleValue 最大值就是 items 中所有元素 power 值的和!据此,我们可以轻松写一个从 angleValue 值找到对应选中 item 的方法:
private func findSltItem() -> Item? { guard let slt = angleValue else { return nil } var sum = 0 // 若 angleValue 小于第一个 item.power ,则表示选择的是图表中首张“大饼”! var sltItem = items.first for item in items { sum += item.power // 试探正确选中的饼图元素 if sum >= slt { sltItem = item break } } return sltItem}
我们现在可以根据饼图中当前选中的 angleValue 值,轻松找到对应的 Item 了:
struct ContentView: View { // 省略其它状态定义... @Query private var items: [Item] @State private var angleValue: Int? @State private var sltItem: Item? var body: some View { NavigationView { List { Chart(items) { item in SectorMark(angle: .value("power", item.power), innerRadius: .ratio(innerRadius), angularInset: 3.0 ) .cornerRadius(10) .foregroundStyle(by: .value("name", item.name)) .opacity(sltItem?.id == item.id ? 1.0 : 0.3) } .onChange(of: angleValue){ old,new in withAnimation { if let item = findSltItem() {if item == sltItem { // 点击已被选中的元素时取消选择 sltItem = nil}else{ sltItem = item} } } }.padding(.vertical, 50) ForEach(items) {...} } .navigationTitle("饼图演示") } }}
效果如下:
看来为 WWDC 官方代码填坑的感觉也很不错哦😘💯
在本篇博文中,我们介绍了 WWDC 23 最新 SwiftUI 5.0(iOS 17)中关于图表的新体验,学习了如何创建饼图(Pie)和实现 Charts 元素的选中功能,小伙伴们还不赶快操练起来!
感谢观赏,再会!😎
--结束END--
本文标题: iOS 17(SwiftUI 5.0)带来的图表(Charts)新类型:“大饼”与“甜甜圈”
本文链接: https://lsjlt.com/news/401284.html(转载时请注明来源链接)
有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341
2024-01-21
2023-10-28
2023-10-28
2023-10-27
2023-10-27
2023-10-27
2023-10-27
回答
回答
回答
回答
回答
回答
回答
回答
回答
回答
0