SwiftUI之深入解析如何使用SwiftUI Charts创建折线图

一、简单折线图

  • 苹果在 WWWDC 2022 上推出了 SwiftUI 图表,这使得在 SwiftUI 视图中创建图表变得异常简单。图表是以丰富的格式呈现可视化数据的一种很好的方式,而且易于理解。本文展示了如何用比以前从头开始创建同样的折线图少得多的代码轻松创建折线图,此外自定义图表的外观和感觉以及使图表中的信息易于访问也是非常容易的。
  • 从包含王者荣耀全部英雄的登场率开始,类似于在 SwiftUI 中创建折线图中使用的数据,定义一个结构来保存英雄和登场率的步数,并创建一个数组:
struct KingHeroWinRate: Identifiable {
    
    let id = UUID()
    let hero: String
    let winRate: Double
    
    init(hero: String, winRate: Double) {
        self.hero = hero;
        self.winRate = winRate;
    }
}

let currentHero: [KingHeroWinRate] = [
    KingHeroWinRate(hero: "孙悟空", winRate: 0.140),
    KingHeroWinRate(hero: "玄奘", winRate: 0.186),
    KingHeroWinRate(hero: "猪八戒", winRate: 0.111),
    KingHeroWinRate(hero: "嫦娥", winRate: 0.187),
    KingHeroWinRate(hero: "杨戬", winRate: 0.084)
]
  • 要创建一个折线图,为英雄登场率数据中的每个元素创建一个带有 LineMark 的图表,在 LineMark 的 X 值中指定英雄,在 Y 值中指定登场率,需要注意的是需要导入 Charts 框架。这就为英雄登场率数据创建了一个线形图,由于只有一个系列的数据,ForEach 可以省略,数据可以直接传递给 Chart 初始化器,两个部分都产生相同的折线图:
import SwiftUI
import Charts

struct ContentView: View {

    var body: some View {
        VStack {
            GroupBox("王者荣耀英雄登场率排行") {
                Chart {
                    ForEach(currentHero) {
                        LineMark(
                            x: .value("英雄", $0.hero),
                            y: .value("胜率", $0.winRate)
                        )
                    }
                }
            }
            
            GroupBox("王者荣耀英雄登场率排行") {
                Chart(currentHero) {
                    LineMark(
                        x: .value("英雄", $0.hero),
                        y: .value("胜率", $0.winRate)
                    )
                    
                }
            }
		}
    }
}
  • 使用 SwiftUI Charts 创建折线图显示英雄登场率的效果如下:

在这里插入图片描述

二、其它图表

  • SwiftUI Charts 有许多可用的图表选项,这些可以通过将图表标记从 LineMark 改为其他类型的标记(如 BarMark)来生成条形图:
struct ContentView: View {

    var body: some View {
        VStack {
            GroupBox("王者荣耀英雄登场率排行") {
                Chart {
                    ForEach(currentHero) {
                        LineMark(
                            x: .value("英雄", $0.hero),
                            y: .value("胜率", $0.winRate)
                        )
                    }
                }
            }
            GroupBox ("王者荣耀英雄登场率排行") {
                Chart(currentHero) {
                    BarMark(
                        x: .value("英雄", $0.hero),
                        y: .value("胜率", $0.winRate)
                    )
                }
            }
            GroupBox ("王者荣耀英雄登场率排行") {
                Chart(currentHero) {
                    PointMark(
                        x: .value("英雄", $0.hero),
                        y: .value("胜率", $0.winRate)
                    )
                }
            }
            GroupBox ("王者荣耀英雄登场率排行") {
                Chart(currentHero) {
                    RectangleMark(
                        x: .value("英雄", $0.hero),
                        y: .value("胜率", $0.winRate)
                    )
                }
            }
            GroupBox ("王者荣耀英雄登场率排行") {
                Chart(currentHero) {
                    AreaMark(
                        x: .value("英雄", $0.hero),
                        y: .value("胜率", $0.winRate)
                    )
                }
            }
        }
    }
}
  • 效果如下:

在这里插入图片描述

三、增加折线图的可访问性

  • 将图表植入 SwiftUI 的一个好处是,可以很容易地使用可访问性修饰符使图表变得可访问。为 KingHeroWinRate 添加一个计算属性,将数据返回为一个字符串,可由 accessibilityLabel 使用,然后为图表中的每个标记添加可访问性标签和值:
struct KingHeroWinRate: Identifiable {
    
    let id = UUID()
    let hero: String
    let winRate: Double
    
    init(hero: String, winRate: Double) {
        self.hero = hero;
        self.winRate = winRate;
    }
    
    var heroNameString: String {
        return hero;
    }
}
  • 第一次尝试添加这两个系列的数据没有按预期显示:
struct ContentView: View {

    var body: some View {
        VStack {
            GroupBox("王者荣耀英雄登场率排行") {
                Chart {
                    ForEach(heroData, id: \.period) {
                        ForEach($0.data) {
                            LineMark(
                                x: .value("英雄", $0.hero),
                                y: .value("登场率", $0.winRate)
                            )
                            .accessibilityLabel($0.heroNameString)
                            .accessibilityValue("\($0.winRate) winRate")
                        }
                    }
                }
            }
        }
    }
}

在这里插入图片描述

四、为折线图添加多个数据序列

  • 折线图是比较两个不同系列数据的好方法,创建第二个系列,即另外一组英雄的登场率,并将这两个系列添加到折线图中:
let previousHero: [KingHeroWinRate] = [
    KingHeroWinRate(hero: "刘备", winRate: 0.063),
    KingHeroWinRate(hero: "关羽", winRate: 0.071),
    KingHeroWinRate(hero: "赵云", winRate: 0.079),
    KingHeroWinRate(hero: "张飞", winRate: 0.061),
    KingHeroWinRate(hero: "黄忠", winRate: 0.061)
]

let currentHero: [KingHeroWinRate] = [
    KingHeroWinRate(hero: "孙悟空", winRate: 0.140),
    KingHeroWinRate(hero: "玄奘", winRate: 0.186),
    KingHeroWinRate(hero: "猪八戒", winRate: 0.111),
    KingHeroWinRate(hero: "嫦娥", winRate: 0.187),
    KingHeroWinRate(hero: "杨戬", winRate: 0.084)
]

let heroData = [
    (period: "Current Hero", data: currentHero),
    (period: "Previous Hero", data: previousHero)
]
  • 第一次尝试添加这两个系列的数据没有按预期显示:
        GroupBox("王者荣耀英雄登场率排行") {
            Chart {
                ForEach(heroData, id: \.period) {
                    ForEach($0.data) {
                        LineMark(
                            x: .value("英雄", $0.heroBranching),
                            y: .value("登场率", $0.winRate)
                        )
                        .accessibilityLabel($0.heroBranching)
                        .accessibilityValue("\($0.winRate) winRate")
                    }
                }
            }
        }

在这里插入图片描述

五、显示登场率系列

  • 在折线图中显示多个基于英雄的登场率系列,最初尝试在折线图中显示多组数据的问题是 X 轴使用了英雄,当前的英雄紧接着上一部分英雄,因此每一个点都是沿着 X 轴线性递增绘制的。有必要只用分路作为 X 轴的数值,这样所有的英雄都在同一个 X 坐标上绘制。
  • 在 KingHeroWinRate 中添加另一个计算属性,以便以字符串格式返回英雄的分路:
struct KingHeroWinRate: Identifiable {
    
    let id = UUID()
    let hero: String
    let winRate: Double
    
    init(hero: String, winRate: Double) {
        self.hero = hero;
        self.winRate = winRate;
    }
    
    var heroNameString: String {
        return hero;
    }
    
    var heroBranching: String {
        if (heroNameString == "孙悟空" || heroNameString == "刘备") {
            return "打野";
        } else if (heroNameString == "玄奘" || heroNameString == "关羽") {
            return "中路";
        } else if (heroNameString == "猪八戒" || heroNameString == "赵云") {
            return "上路";
        } else if (heroNameString == "嫦娥" || heroNameString == "张飞") {
            return "辅助";
        } else if (heroNameString == "杨戬" || heroNameString == "黄忠") {
            return "下路";
        } else {
            return "";
        }
   }
    
}
  • 此 heroBranching 用于图表中 LineMarks 的 x 值。另外,前景的样式设置为基于 KingHeroWinRate 数组的周期,折线图使用 x 轴的分路来显示英雄,以便在分路之间进行比较:
struct ContentView: View {

    var body: some View {       
        VStack {
            GroupBox("王者荣耀英雄登场率排行") {
                Chart {
                    ForEach(heroData, id: \.period) { hero in
                        ForEach(hero.data) {
                            LineMark(
                                x: .value("英雄", $0.heroBranching),
                                y: .value("登场率", $0.winRate)
                            )
                            .foregroundStyle(by: .value("branch", hero.period))
                            .accessibilityLabel($0.heroBranching)
                            .accessibilityValue("\($0.winRate) winRate")
                        }
                    }
                }
                .frame(height:400)
            }
            .padding()
            Spacer()
        }
    }
}

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/318734.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【深度学习】Anaconda3 + PyCharm 的环境配置 3:GitHub 项目运行前的环境配置

前言 文章性质:实操记录 💻 主要内容:主要记录了运行 GitHub 项目前的环境配置过程,包括创建并激活新的虚拟环境、安装 torch 和 torchvision,在 PyCharm 中使用新建的虚拟环境,根据项目源代码提供的 requi…

Xtuner大模型微调

Xtuner大模型微调 一、课程笔记 文档链接:https://github.com/InternLM/tutorial/blob/main/xtuner/README.md 视频链接: https://www.bilibili.com/video/BV1yK4y1B75J/ 大模型微调 大模型的训练利用了各类数据,可以说是一个通才&#xff…

Sqoop的增量数据加载策略与示例

当使用Apache Sqoop进行数据加载时,增量数据加载策略是一个关键的话题。增量加载可以仅导入发生变化的数据,而不必每次都导入整个数据集,这可以显著提高任务的效率。本文将深入探讨Sqoop的增量数据加载策略,提供详细的示例代码&am…

如何调整 Windows 11 任务栏位置、对齐方式,及自定义任务栏

更新于:2023-11-22 分类:Windows 阅读(115407) 评论(12) 如果你是 Windows 11 用户中的一员,一定在不断尝试它的新功能。Windows 11 操作系统采用了全新设计的外观,具有重新设计的 Windows 资源管理器、圆润的窗口边缘和默认将应用…

【期末不挂科-C++考前速过系列P5】大二C++实验作业-多态性(3道代码题)【解析,注释】

前言 大家好吖,欢迎来到 YY 滴C考前速过系列 ,热烈欢迎! 本章主要内容面向接触过C的老铁 主要内容含: 欢迎订阅 YY滴C专栏!更多干货持续更新!以下是传送门! YY的《C》专栏YY的《C11》专栏YY的《…

六、新建窗体时,几种窗体的区别

新建窗体时,会有几种类型的选项,很多同学不明白其中的意思,我们在本章节中详细介绍一下几种窗体的区别。 窗体的类型分以下几种 Dialog with Buttons Bottom 带按钮的对话框,按钮在底部 Dialog with Buttons Right 带按钮的对话框…

MySQL面试题 | 06.精选MySQL面试题

🤍 前端开发工程师(主业)、技术博主(副业)、已过CET6 🍨 阿珊和她的猫_CSDN个人主页 🕠 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 🍚 蓝桥云课签约作者、已在蓝桥云…

Open3D 计算点云质心和中心(18)

Open3D 计算点云质心和中心(18) 一、算法介绍二、算法实现1.代码2.结果一、算法介绍 质心和中心是有所区别的,点云质心可以看作每个点的坐标均值,点云中心可以看作点云所在包围盒的中心,这也是上一章坐标最值的常用方法,下面就两种方法进行实现(图例,大概就是这个意思…

SFP/SFP+/QSFP/QSFP+光模块和GTP/GTX/GTH/GTZ/GTY/GTM高速收发器

SFP/SFP/QSFP/QSFP光模块和GTP/GTX/GTH/GTZ/GTY/GTM高速收发器 SFP/SFP/QSFP/QSFP光模块概述SFPSFPQSFPQSFP关键参数说明 GTP/GTX/GTH/GTZ/GTY/GTM高速收发器区别XILINX 7系列FPGA中高速收发器使用 SFP/SFP/QSFP/QSFP光模块 概述 SFP( small form-factor pluggabl…

部分城市公交站点数据,Shp+excel格式数据,2020年,几何类型为点

随着城市的发展和人口的增长,公共交通成为了人们出行的重要方式之一。而公交站点作为公共交通的重要组成部分,其数据信息的获取和分析对于城市规划和管理具有重要意义。 今天来分享一下部分城市公交站点数据: 首先先了解下该数据的基本信息 …

Error: error:0308010C:digital envelope routines::unsupported的解决方案

因为最近安装了pnpm对node版本有要求,升级了node版本是18以后,在运行之前的项目,就跑不起来了,报错如下: Error: error:0308010C:digital envelope routines::unsupported解决方案一: node版本切换到16版…

MATLAB - 机器人关节空间运动模型

系列文章目录 前言 关节空间运动模型描述了在闭环关节空间位置控制下机械手的运动,在关节空间运动模型(jointSpaceMotionModel)对象和关节空间运动模型块中使用。 机器人机械手是典型的位置控制设备。要进行关节空间控制,需要指…

LLVM系列(1): 在微软Visual Studio下编译LLVM

参考链接: Getting Started with the LLVM System using Microsoft Visual Studio — LLVM 18.0.0git documentation 1.安装visualstudio,版本需要大于vs2019 本机环境已安装visual studio2022,省略 2安装Makefile,版本需要大…

定时器问题(vue的问题)

我在a页面写一个定时,让他每秒钟打印一个1,然后跳转到b页面,此时可以看到,定时器依然在执行。这样是非常消耗性能的。如下图所示: 解决方法1 首先我在data函数里面进行定义定时器名称: data() {return {t…

《ARM Linux内核源码剖析》读书笔记——0号进程(init_task)的创建时机

最近在读《ARM Linux内核源码剖析》,一直没有看到0号进程(init_task进程)在哪里创建的。直到看到下面这篇文章才发现书中漏掉了set_task_stack_end_magic(&init_task)这行代码。 下面这篇文章提到:start_kernel()上来就会运行 set_task_…

五、带登录窗体的demo

做了一个简单的带登录窗体的demo,有用户名和密码不能为空的验证,原理是在main.cpp的主函数入口处: 1、将默认的MainWindow主窗体注释。 2、新建一个formlogin登录窗体,在主函数中先运行登录窗体。 3、在登录窗体中引用MainWind…

Javaweb之SpringBootWeb案例新增部门的详细解析

2.3 删除部门 查询部门的功能我们搞定了,下面我们开始完成删除部门的功能开发。 2.3.1 需求 点击部门列表后面操作栏的 "删除" 按钮,就可以删除该部门信息。 此时,前端只需要给服务端传递一个ID参数就可以了。 我们从接口文档中也…

【PACS Web系统】全网首发JAVA开发PACS医疗影像工作站

目录 业务分析: 市场前景: Web版相对单机版优势: 主干功能: RBAC用户权限管理、服务监控、字典维护、通知公告等基础模块; 手动上传Dicom文件/文件夹,及接收Dicom服务器的Dicom文件集功能&#xff1b…

Vue基知识四

本文对前边几章所学习的内容,以案例的形式做一个总结 一 TodoList案例 即待办事项案例,效果如下 1.1 组件化编码流程(通用) 这是编码时的通用流程,以后也可以按照这个流程来写代码(熟悉后这个流程的顺…

管理软件供应链中网络安全工具蔓延的三种方法

软件开发组织不断发展,团队成长,项目数量增加。技术堆栈发生变化,技术和管理决策变得更加分散。 在这一演变过程中,该组织的 AppSec 工具组合也在不断增长。在动态组织中,这可能会导致“工具蔓延”。庞大的 AppSec 工…