SwiftUI Minimalist Tutorial 12: Use of List and ForEach Loop

Get into the habit of writing together! This is the 12th day of my participation in the "Nuggets Daily New Plan · April Update Challenge", click to view the details of the event .

Foreword: It’s almost 12 o’clock in the writing of this article, and it has been written for almost 2 hours. In general, it is not so simple to explain a knowledge point clearly. First of all, the language should not be too complicated, and there should not be too many proper nouns. , if there are nouns that can't be avoided, try to find a way to give an example. Sure enough, the road to transformation is always difficult and lonely.

Word of the day: Don't try to convince readers, don't advance the topic in a sensible way.

In this chapter, you will learn how to create a list using loop traversal and dynamically generate lists with custom parameters.

If you have been exposed to UIKit, you should have used the tableView component to create lists. Many of the apps we use in our lives are basically in the form of lists.

For example, the information list of Nuggets, mobile phone system settings, music list, etc...

214.png

Basically, most apps, especially information apps, have lists.

In SwiftUI, we replace the original tableView with List, which makes the code more concise and easy to understand.

Due to the different applications of the List component in different scenarios, this chapter is divided into 4 parts to explain.

1. Simple text list;

2. Picture + text list;

3. List data integration;

4. Use of the Identifiable protocol;

Part 1: Simple Text List

First, let's create a new project called SwiftUIList.

194.png

We create a simple list in the ContentView.swift file.

The construction of List is very similar to the VStack we learned before, wrapping the content in it to form a list.

struct ContentView: View {
    var body: some View {

        //简单的列表
        List {
            Text(“第1页")
            Text(“第2页")
            Text(“第3页")
            Text(“第4页")
        }
    }
}
复制代码

195.png

We see that the List is full of Text text, and only the content is different.

At this time, we can use ForEach to extract the code, so we don't need to write so many similar codes.

struct ContentView: View {
    var body: some View {

        // 简单的列表
        List {
            ForEach(1 ... 4, id: \.self) { index in
                Text("第 \(index)页")
            }
        }
    }
}
复制代码

196.png

When using ForEach to traverse to create a view, you need to use id to identify the content. When the content inside changes, ForEach can automatically update the UI.

Just read the code content:

We pass ForEach a range of values ​​to loop through the generated list.

And its id (identifier) ​​is set to the value itself, which is 1, 2, 3, 4 set earlier.

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

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

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

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

struct ContentView: View {
    var body: some View {

        // 简单的列表
        List {
            ForEach(1 ... 4, id: \.self) {
                Text("第 \($0)页")
            }
        }
    }
}
复制代码

197.png

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

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

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

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

207.png

首先分析下它的结构。

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

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

199.png

200.png

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

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

//定义数组,存放数据

var myImages = ["weixin","weibo","qq","phone","mail"]
var myNames = ["这是微信","这是微博","这是QQ","这是电话","这是邮箱"]
复制代码

208.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])
            }
        }
    }
}
复制代码

209.png

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

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

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

第三部分:列表数据整合

下面,我们再进阶一下。

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

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

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

我们回到UI稿中。

207.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)
    }
}
复制代码

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

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

210.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")
]
复制代码

211.png

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

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

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

这时候,我们该怎么办?

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

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

struct Message {
    var id = UUID()
    var image: String
    var name: String
}
复制代码

In the code, we add the id property and initialize it with a unique identifier.

The UUID() function is used to generate a globally unique random identifier.

UUIDs are made up of 128-bit numbers, so theoretically, the chance of having two identical identifiers is virtually nil.

Then, we use the id in the body code to refer to the id of the Message structure.

// 列表

List(Messages, id: \.id) { Message in
    HStack {
        Image(Message.image)
            .resizable()
            .frame(width: 40, height: 40)
            .cornerRadius(5)
    Text(Message.name)
    }
}
复制代码

In this way, we have completed the uniqueness of the List data source.

212.png

One more knowledge point.

We can also set the structure Message to follow the Identifiable protocol.

In this way, a struct that conforms to Identifiable can automatically track its id as a unique identifier, and we don't need to specify an id in the body.

The complete code is as follows:

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
}
复制代码

213.png

congratulations! Completed the learning of List!

If this column is helpful to you, please like, comment, and follow~

Guess you like

Origin juejin.im/post/7085515068586065950