SwiftUI极简教程12:List列表和ForEach循环的使用

简介: SwiftUI极简教程12:List列表和ForEach循环的使用

在本章中,你将学会如何使用循环遍历的方式创建一个列表,并可自定义参数动态生成列表。

如果你接触过UIKit的话,应该会用过tableView组件创建列表,我们生活中用的很多App基本都是列表的形式。

例如掘金的信息列表、手机系统设置、音乐列表等等……

image.png

基本上大多数App,特别是资讯类App都少不了列表的存在。

而在SwiftUI中,我们用List代替了原有tableView,使得代码更加简洁易懂。

由于List组件在不同场景下的应用不同,本章节分成4个部分讲解。

1、简单文字列表;

2、图片+文字列表;

3、列表数据整合;

4、Identifiable协议的使用;


第一部分:简单文字列表


首先,我们先创建一个新项目,命名为SwiftUIList。


image.png

我们在ContentView.swift文件中,创建一个简单的列表。

List的构造方式和之前学习的VStack很类似,将内容包裹在里面形成列表。

struct ContentView: View {
    var body: some View {
        //简单的列表
        List {
            Text(“第1页")
            Text(“第2页")
            Text(“第3页")
            Text(“第4页")
        }
    }
}

image.png

我们看到List里面都是Text文本,而且只是内容不同。

这时候,我们可以使用ForEach的方式把代码抽离出来,也就不需要写那么多相似的代码。

struct ContentView: View {
    var body: some View {
        // 简单的列表
        List {
            ForEach(1 ... 4, id: \.self) { index in
                Text("第 \(index)页")
            }
        }
    }
}


image.png

在使用ForEach遍历创建视图时,需要用id来标识内容,当里面的内容发现变化时,ForEach就可以自动更新UI。

简单来读一下代码内容:

我们传递给ForEach一个范围的值,用来循环遍历生成列表。

而它的id(标识符)被设置为值本身self,也就是前面设置的1、2、3、4。

然后用index参数存储循环的值。

我们在这里遍历了4次,每一次展示一个Text,Text里面的文字是“第”+{index}+“页”,index的参数值从1~4;

这样,我们就得到了一个列表。

当然,还有更简单的遍历方法。

struct ContentView: View {
    var body: some View {
        // 简单的列表
        List {
            ForEach(1 ... 4, id: \.self) {
                Text("第 \($0)页")
            }
        }
    }
}

image.png

在这里,我们省略索引参数index,而使用简化的$0,它引用闭包的第一个参数,直接将数据集合传递给List。

这样,也可以达到列表的效果,而且使得代码更加简单。


第二部分:图片+文字列表


好,下面进阶一下,我们尝试完成下面的UI设计稿。

image.png

首先分析下它的结构。

一个列表里,有Image、Text,他们是横向HStack排布。

我们先在Assets.xcassets导入我们所需的图片。

image.png

并且我们已经提前给图片命好名了,方便我们接下来使用它们。

我们回到ContentView.swift文件中,创建2个数组,存放我们的图片和文字。


//定义数组,存放数据
var myImages = ["weixin","weibo","qq","phone","mail"]
var myNames = ["这是微信","这是微博","这是QQ","这是电话","这是邮箱"]

image.png

我们看到报错了,但又没有完全报错。

这是因为我们定义了一个动态数组,但在代码中没有用到,所以系统告诉我们定义的数组名称没有被使用罢了。

没事的,我们继续。

我们在body里面创建我们需要的代码。


struct ContentView: View {
    //定义数组,存放数据
    var myImages = ["weixin","weibo","qq","phone","mail"]
    var myNames = ["这是微信","这是微博","这是QQ","这是电话","这是邮箱"]
    var body: some View {
        // 列表
        List(myImages.indices, id: \.self) { index in
            HStack {
                Image(self.myImages[index])
                    .resizable()
                    .frame(width: 40, height: 40)
                    .cornerRadius(5)
                Text(self.myNames[index])
            }
        }
    }
}


image.png

我们还是构建了一个列表,不过使用myImages作为目录,也就是myImages.indices。

然后图片遍历用myImages数组,文字遍历用myNames数组。

是不是很简单,比起以前用UIKit的时候,要给cell协议和声明,SwiftUI几行代码就搞定了。

第三部分:列表数据整合


下面,我们再进阶一下。

上面的代码中,我们发现如果是图片+文字,那么我们创建了2个数组,如果是更加复杂的场景,我们岂不是要建立一堆的数组数据?

不不不,这肯定不够优雅。

最好的方式应该是,无论我们多少数组数据,我们都用一个数组包裹住。

我们回到UI稿中。

image.png

有没有办法,把Image和Text定义出来,然后Image和Text是一个数组?

有的!这时候,我们需要创建一个结构体,叫做Message,并定义好里面的变量。

代码如下:


struct Message {
    var name: String
    var image: String
}


使用这个Message结构体,我们将原来的myImages、myNames数组组合成一个数组。

我们定义了一个数组Messages,它里面的内容是Message结构体。

代码如下:


// 定义数组,存放数据
    var Messages = [
        Message(name: "这是微信", image: "weixin"),
        Message(name: "这是微博", image: "weibo"),
        Message(name: "这是QQ", image: "qq"),
        Message(name: "这是电话", image: "phone"),
        Message(name: "这是邮箱", image: "mail")
    ]


构建好了以后,我们回到body里面,把里面的引用的参数值换成数组Messages,使用结构体Message遍历数据,List中使用image属性作为唯一的标识符。

同时,要把里面Image的参数引用结构体Message的image参数,Text引用结构体Message的name参数。

代码如下:


// 列表
List(Messages, id: \.image) { Message in
    HStack {
        Image(Message.image)
            .resizable()
            .frame(width: 40, height: 40)
            .cornerRadius(5)
        Text(Message.name)
    }
}


那么,最终的结果和我们之前做的效果是一样的。

唯一不一样的是,我们的代码看起来优雅多了。


image.png

小结一下:

我们创建了一个结构体Message,它定义了2个变量,1个是image图片,是Strring类型,另一个是name名称,也是Strring类型。

然后我们定义个一个数组Messages(注意加了S),这个数组里面是结构体Message,并赋予了结构体里面2个变量的值;

然后在在body主代码块List里面,用Messages数据作为引用值数据,然后用结构体Message遍历数据。

最后把里面的Image和Text中的值换成结构体中变量的值。


第四部分:Identifiable协议的使用


完成第三部分以后,我们的代码List就完美了么?

并不!

因为这个:

List(Messages, id: \.image)


我们使用了image为Messages数组的唯一标识符,也就是List是通过image的唯一性找到对应数组的数据。

这样,我们会面临一个问题,如果我有2个图片是一样的,但是它的name不一样。

我们尝试把Messages数组的数据换成下面这样:


// 定义数组,存放数据
var Messages = [
    Message(name: "这是微信", image: "weixin"),
    Message(name: "这是第二个微信号", image: "weixin")
]


image.png

我们发现,如果我们使用image作为id,也就是唯一标识符的话。

如果我的数组里有2个image,如果它们的值相同,那么SwiftUI会认为这两个是同一个东西。


也就是,我都是图片都是“微信”,但名称不一样,但计算机认为两个都是微信,而且值一样,这是因为image作为id是唯一的。

这时候,我们该怎么办?


我们期望的结果是,Messages数组里,每一个结构体的数据都是唯一的。

那么我们就不能在body构建唯一的标识符,应该在传值之前就在Message结构体里构建唯一的id。

struct Message {
    var id = UUID()
    var image: String
    var name: String
}


在代码中,我们添加了id属性,并用唯一标识符初始化它。

UUID()函数用于生成一个全局惟一的随机标识符。

UUID由128位数字组成,因此从理论上讲,拥有两个相同标识符的可能性几乎为零。

然后,我们把body代码中的id,引用Message结构体的id。


// 列表
List(Messages, id: \.id) { Message in
    HStack {
        Image(Message.image)
            .resizable()
            .frame(width: 40, height: 40)
            .cornerRadius(5)
    Text(Message.name)
    }
}


这样,我们就完成了List数据源的唯一。


image.png

再科普一个知识点。

我们还可以设置结构体Message遵循Identifiable协议。

这样,遵循Identifiable的结构体就可以自动跟踪它的id作为唯一标识符,我们也就不需要在body中指定id了。

完整代码如下:


import SwiftUI
struct ContentView: View {
    // 定义数组,存放数据
    var Messages = [
        Message(image: "weixin", name: "这是微信"),
        Message(image: "weixin", name: "我的第二个微信号")
    ]
    var body: some View {
        // 列表
        List(Messages) { Message in
            HStack {
                Image(Message.image)
                    .resizable()
                    .frame(width: 40, height: 40)
                    .cornerRadius(5)
                Text(Message.name)
            }
        }
    }
}
struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
struct Message: Identifiable {
    var id = UUID()
    var image: String
    var name: String
}


image.png

恭喜你!完成了List的学习!

相关文章
|
6天前
|
数据挖掘 大数据 数据处理
python--列表list切分(超详细)
通过这些思维导图和分析说明表,您可以更直观地理解Python列表切分的概念、用法和实际应用。希望本文能帮助您更高效地使用Python进行数据处理和分析。
22 14
|
24天前
|
大数据 UED
「Mac畅玩鸿蒙与硬件16」鸿蒙UI组件篇6 - List 和 Grid 组件展示数据列表
List 和 Grid 是鸿蒙开发中的核心组件,用于展示动态数据。List 适合展示垂直或水平排列的数据列表,而 Grid 则适用于展示商品或图片的网格布局。本篇将展示如何封装组件,并通过按钮实现布局切换,提升界面的灵活性和用户体验。
60 9
「Mac畅玩鸿蒙与硬件16」鸿蒙UI组件篇6 - List 和 Grid 组件展示数据列表
|
8天前
|
数据挖掘 大数据 数据处理
python--列表list切分(超详细)
通过这些思维导图和分析说明表,您可以更直观地理解Python列表切分的概念、用法和实际应用。希望本文能帮助您更高效地使用Python进行数据处理和分析。
23 10
|
1月前
|
索引 Python
List(列表)
List(列表)。
36 4
|
2月前
|
测试技术 开发者 Python
在 Python 中创建列表时,应该写 `[]` 还是 `list()`?
在 Python 中,创建列表有两种方法:使用方括号 `[]` 和调用 `list()` 函数。虽然两者都能创建空列表,但 `[]` 更简洁、高效。性能测试显示,`[]` 的创建速度比 `list()` 快约一倍。此外,`list()` 可以接受一个可迭代对象作为参数并将其转换为列表,而 `[]` 则需要逐一列举元素。综上,`[]` 适合创建空列表,`list()` 适合转换可迭代对象。
在 Python 中创建列表时,应该写 `[]` 还是 `list()`?
|
1月前
|
JavaScript 数据管理 虚拟化
ArkTS List组件基础:掌握列表渲染与动态数据管理
在HarmonyOS应用开发中,ArkTS的List组件是构建动态列表视图的核心。本文深入探讨了List组件的基础,包括数据展示、性能优化和用户交互,以及如何在实际开发中应用这些知识,提升开发效率和应用性能。通过定义数据源、渲染列表项和动态数据管理,结合虚拟化列表和条件渲染等技术,帮助开发者构建高效、响应式的用户界面。
191 2
|
2月前
|
NoSQL 关系型数据库 MySQL
Redis 列表(List)
10月更文挑战第16天
30 2
|
6月前
|
安全 Java
java线程之List集合并发安全问题及解决方案
java线程之List集合并发安全问题及解决方案
1001 1
|
5月前
|
Java API Apache
怎么在在 Java 中对List进行分区
本文介绍了如何将列表拆分为给定大小的子列表。尽管标准Java集合API未直接支持此功能,但Guava和Apache Commons Collections提供了相关API。
|
5月前
|
运维 关系型数据库 Java
PolarDB产品使用问题之使用List或Range分区表时,Java代码是否需要进行改动
PolarDB产品使用合集涵盖了从创建与管理、数据管理、性能优化与诊断、安全与合规到生态与集成、运维与支持等全方位的功能和服务,旨在帮助企业轻松构建高可用、高性能且易于管理的数据库环境,满足不同业务场景的需求。用户可以通过阿里云控制台、API、SDK等方式便捷地使用这些功能,实现数据库的高效运维与持续优化。