SwiftUI:使自定义类型符合 Comparable 协议
在编写Swift代码时,我们会想到很多东西。例如,如果我们写4 < 5
,我们期望返回true
– Swift的开发人员(以及LLVM,Swift后面的更大的编译器项目)已经完成了检查该计算是否正确的所有的艰苦工作,因此我们不必为此担心。
但是Swift真正擅长的是使用协议和协议扩展将功能扩展到很多地方。例如,我们知道4 < 5
是正确的,因为我们能够比较两个整数并确定第一个整数是在第二个整数之前还是之后。Swift将功能扩展到整数数组:我们可以比较数组中的所有整数,以确定每个整数应该在另一个整数之前还是之后。然后,Swift使用该结果对数组进行排序。
因此,在Swift中,我们希望这种代码可以正常工作:
struct ContentView: View {
let values = [1, 5, 3, 6, 2, 9].sorted()
var body: some View {
List(values, id: .self) {
Text(String($0))
}
}
}
我们不需要告诉sorted()
应该如何工作,因为它了解整数数组的工作方式。
现在考虑这样的结构体:
struct User: Identifiable {
let id = UUID()
let firstName: String
let lastName: String
}
我们可以将这些用户组成一个数组,并在这样的List
中使用它们:
struct ContentView: View {
let users = [
User(firstName: "Arnold", lastName: "Rimmer"),
User(firstName: "Kristine", lastName: "Kochanski"),
User(firstName: "David", lastName: "Lister"),
]
var body: some View {
List(users) { user in
Text("(user.lastName), (user.firstName)")
}
}
}
这样就可以了,因为我们使User
结构体符合Identifiable
协议。
但是,如果我们想按排序顺序显示这些用户呢?如果我们将代码修改为这样,将无法正常工作:
let users = [
User(firstName: "Arnold", lastName: "Rimmer"),
User(firstName: "Kristine", lastName: "Kochanski"),
User(firstName: "David", lastName: "Lister"),
].sorted()
Swift不了解sorted()
在这里的含义,因为它不知道是按名字,姓氏,两者都排序还是其他的条件排序。
之前,我向您展示了如何为sorted()
提供闭包以自行进行排序,并且可以在此处使用相同的代码:
let users = [
User(firstName: "Arnold", lastName: "Rimmer"),
User(firstName: "Kristine", lastName: "Kochanski"),
User(firstName: "David", lastName: "Lister"),
].sorted {
$0.lastName < $1.lastName
}
绝对可行,但是由于两个原因,它不是理想的解决方案。
首先,这是模型数据,这意味着它正在影响我们处理User
结构体的方式。该结构体及其属性是我们的数据模型,在一个开发良好的应用程序中,我们实际上并不想告诉模型在我们的SwiftUI代码中应如何表现。SwiftUI代表了我们的View,即我们的布局,如果我们在其中放置模型代码,那么事情就会变得混乱。
其次,如果我们要在多个位置对用户数组进行排序,会发生什么?您可能会复制或粘贴一次或两次,然后意识到自己只是在为自己创建一个问题:如果最终更改了排序逻辑,以便在姓氏相同的情况下也使用firstName
,则需要搜索遍历所有代码,以确保更新所有闭包。
Swift有更好的解决方案。整数数组获得一个没有参数的简单sorted()
方法,因为Swift知道如何比较两个整数。用编码术语来说,Int
符合Comparable
协议,这意味着它定义了一个接受两个整数的函数,如果第一个应在第二个之前排序,则返回true
。
我们可以使自己的类型符合Comparable
,并且当我们这样做时,我们还将获得没有参数的sorted()
方法。这需要两个步骤:
- 将
Comparable
添加到User
的定义。 - 添加一个名为
<
的方法,该方法需要两个用户,并且如果第一个应在第二个之前排序,则返回true
。
这是代码:
struct User: Identifiable, Comparable {
let id = UUID()
let firstName: String
let lastName: String
static func < (lhs: User, rhs: User) -> Bool {
lhs.lastName < rhs.lastName
}
}
那里没有很多代码,但是还有很多要解决的东西。
首先,是的,该方法仅被称为 <
,它是“小于”运算符。该方法的工作是确定一个用户是否“少于”(在排序意义上)另一个用户,因此我们要向现有的操作符添加功能。这被称为操作符重载,它既是福也是祸。
其次,lhs
和rhs
是“左手边 left-hand side ”和“右手边 right-hand side” 的缩写,它们的使用是因为<
运算符的左侧是一个操作数,右侧是一个操作数。
第三,此方法必须返回一个布尔值,这意味着我们必须确定是否应先将一个对象排序。这里没有“它们相同”的空间——由另一种称为Equatable
的协议处理。
第四,该方法必须标记为静态,这意味着它是直接在User
结构体上调用的,而不是结构体的单个实例。
最后,我们的逻辑非常简单:我们只是将比较传递给我们的一个属性,要求Swift将两个姓氏字符串使用<
。您可以根据需要添加任意数量的逻辑,比较所需的多个属性,但最终需要返回true
或false
。
现在我们的User
结构体符合Comparable
,我们将自动访问sorted()
的无参数版本,这意味着这种代码现在可以使用:
let users = [
User(firstName: "Arnold", lastName: "Rimmer"),
User(firstName: "Kristine", lastName: "Kochanski"),
User(firstName: "David", lastName: "Lister"),
].sorted()
这就解决了我们以前遇到的问题:我们现在将模型功能隔离在结构体本身中,并且不再需要复制和粘贴代码了——我们可以在各处放心的使用sorted()
,因为如果我们更改了算法,那么我们所有的代码都会自动适配。
- 关于 JS 拖拽功能的冲突问题及解决方法
- 使用 SVG 和 JS 创建一个由星形变心形的动画
- 如何使用 Bootstrap 搭建更合理的 HTML 结构
- Java遍历Map对象的四种方式
- java 线程public void run()中值如何返回
- Jackson与spring框架整合的坑
- spring shiro整合时自动注入的问题
- mybatis获取update的id
- 点击!AWD攻防解题技巧在此!
- Docker 基础技术之 Linux namespace 详解
- Centos6下使用yum安装MariaDB
- Linux下部署Samba服务环境的操作记录
- SCP和Rsync远程拷贝的几个技巧
- Linux服务器更换主板后,网卡识别失败的处理方法
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 虚拟机更换JDK版本步骤(Hadoop集群)
- JavaSE重点复习
- [数据结构与算法] 树结构之二叉排序树、平衡二叉树、多路查找树
- 工作后, 你一定不能错过技术之JDK1.8的新特性
- 【Go】剑指offer:二叉树子树的判断
- css中的box-shadow属性详解
- 基于docker快速搭建多节点Hadoop集群
- CSS简笔画:纯CSS绘制一辆婴儿车
- 分布式自增数据库ID
- 【STM32F429开发板用户手册】第27章 STM32F429的定时器应用之TIM1-TIM14的PWM实现
- 数据库基础开源学习教程-android 使用 litepal 操作本地数据库
- 红黑树——动态+静态图
- 一文读懂Python实现张量运算
- javascript之闭包基础了解
- Python中的多处理与多线程:新手简介