SwiftUI里的ForEach使用的注意事项

news2024/9/22 8:50:14

在用Swift编程语言的SwiftUI包设计苹果设备的程序时,经常会用到ForEach函数。这个函数的作用是将一个数据集里面的内容一条一条地取出,罗列在程序的页面上,使用方式的详解见[1]。

但ForEach和一般的循环不同之处在于它要求输入里面的数据集里元素必须是Identifiable的,否则不可使用[2]。所谓Identifiable,就是说输入ForEach里的数据集里的每一个元素必须有一个唯一确定的,不会重复的id号,所以通过该id号,就可找到唯一确定的与之对应的元素,因此若要修改或删除元素,不会删错或在修改时涉及无关的元素。

一、可输入ForEach的数据

本文以一个简单的例子,说明什么样的数据可以输入到ForEach中。在该例子中,输入的数据集合里的每个元素都是一个字母,这些元素被一一加入到List里,形成一个列表如图显示。

下面说明几种将数据输入ForEach的方法:

(一)将字符串列表直接输入ForEach

@State var alphaList = ["a", "b", "c"]
var body: some View {
        List{
            Section(header: Text("Invaild list")){
                ForEach(alphaList){alphaEle in
                    Text(alphaEle)
                }
            } //This is not legal. ForEach should be inputed as a range, or an identifiable. normal list is not ok
       }
}

这一段代码在Swift中将无法运行,因为Swift里的列表里的元素只是字符串,而这些元素未指定id号,所以Swift里无法根据元素的任何信息唯一确定该元素。

所以,不能直接将字符串的列表输入ForEach。

(二)将字符串元素的字符串本身设为其在列表中的id

@State var alphaList = ["a", "b", "c"]
var body: some View {
        List{
           Section(header: Text("Normal list")){
                ForEach(alphaList, id: \.self){alphaEle in
                //alphaList itself is not identifiable, so need to define id. Here the id is just the element title. This is not good because the id can repeat
                    Text(alphaEle)
                }
            }
       }
}

这段代码可以正常运行。因为在ForEach函数里,虽然输入的数据集未能提供每个元素的id,但在ForEach函数的id参数里,对这个信息进行了补充,使用\.self这个Key Path表明每个元素在这个数据集的id号就是这个字符串本身。关于Key Path的概念,见[3]。另外,博文[4]中讲解了Key Path如何使用。

这个方法虽然可行,但并不建议,因为不同元素的字符串本身一旦出现重复,Swift就无法唯一确定每一个id对应的元素了。

(三)直接用区间作为索引号数据集,然后根据索引号提取元素

@State var alphaList2 = ["a", "b", "c"]
var body: some View {
        List{
           Section(header: Text("Normal list")){
               ForEach(0..<alphaList2.count){idx in
                    Text(alphaList2[idx])
                    //This is not good. ForEach, if using a integer range, the range should be constant.
                }
            }
       }
       Button(action: {
            alphaList2.append("ff")
        }, label: {
            Text("Add for extract idx")
        })//This button will fail. 
}

这段代码可以正常运行,因为Swift里的ForEach函数支持区间输入。但这样的输入,要求区间必须固定不变。如果在该程序运行时,alphaList2是一个固定不变的列表,那么这样使用ForEach函数是可以的。但如果在程序运行中,需要添加或删除元素,则不应使用区间输入。

在以上代码中,界面上除了定义一个ForEach的List外,还定义了一个按钮,按下后就会在列表中添加元素。但这样的编程方式,按钮按下后,屏幕上也不会有任何变化,因为ForEach函数如果输入的是区间,则不支持变动的区间。

从动画中可看出,无论如何点击按钮"Add for extract idx",列表里的内容都没有变化。

(四)用区间作为索引号数据集,但添加索引号作为id

@State var alphaList3 = ["a", "b", "c"]
var body: some View {
        List{
          Section(header: Text("Extract Idx with id")){
                ForEach(0..<alphaList3.count, id: \.self){idx in
                    Text(alphaList3[idx])
                    //this is good, because although integer range is used, an id is specified so that the whole input together can be an identifiable
                }
            }
       }
       Button(action: {
            alphaList3.append("ff")
        }, label: {
            Text("Add for extract idx with id")
        })       
}

这段代码可以正常运行,而且列表添加可以正常进行,因为输入ForEach的区间里的每一个元素已经被赋予了id。

从动画中可看出,点击按钮"Add for extract idx with id"后,列表会被添加。

(五)创建一个Identifiable的类,让元素使用这个类

class alpha: Identifiable{
    public var letter:String
    init(_ l:String){
        letter = l
    }
}


@State var alphaList4 = [alpha("a"), alpha("b"), alpha("c")]

var body: some View {
        List{
          Section(header: Text("identifiable letter")){
                ForEach(alphaList4){alphaEle in
                    Text(alphaEle.letter)
                    //this is good, because alphaList4 is identifiable
                }
            }
       }
       Button(action: {
            alphaList4.append(alpha("ff"))
        }, label: {
            Text("Add for identifiable objects")
        })
}

在这段代码中,alphaList4里面的每一个元素都是Identifiable的alpha类元素,所以alphaList4可以直接输入ForEach函数。该代码可以正常运行,且列表添加功能可正常使用。

(六)仍然使用方法(一)但把String类型延伸一个id

Swift中可以对一个已有类型添加一个extension,从而扩充它的属性[5]。这里对String进行扩充。

extension String: Identifiable{
    public var id: String {UUID().description}
    //public var id: String{self} //This kind of id is not suggested
}

这样一来,方法(一)就不再报错了。

二、整个程序及总结

import SwiftUI

class alpha: Identifiable{
    public var letter:String
    init(_ l:String){
        letter = l
    }
}
extension String: Identifiable{
    public var id: String {UUID().description}
    //public var id: String{self} //This kind of id is not suggested
}

struct ListLab: View {
    @State var alphaList = ["a", "b", "c"]
    @State var alphaList2 = ["a", "b", "c"]
    @State var alphaList3 = ["a", "b", "c"]
    @State var alphaList4 = [alpha("a"), alpha("b"), alpha("c")]
    @State var alphaList5 = ["a", "b", "c"]
    var body: some View {
        List{
            //Section(header: Text("Invaild list")){
            //    ForEach(alphaList){alphaEle in
            //        Text(alphaEle)
            //    }
            //} //This is not legal. ForEach should be inputed as a range, or an identifiable. normal list is not ok
            Section(header: Text("Normal list")){
                ForEach(alphaList, id: \.self){alphaEle in
                //alphaList itself is not identifiable, so need to define id. Here the id is just the element title. This is not good because the id can repeat
                    Text(alphaEle)
                }
            }
            Section(header: Text("Extract Idx")){
                ForEach(0..<alphaList2.count){idx in
                    Text(alphaList2[idx])
                    //This is not good. ForEach, if using a integer range, the range should be constant.
                }
            }
            Section(header: Text("Extract Idx with id")){
                ForEach(0..<alphaList3.count, id: \.self){idx in
                    Text(alphaList3[idx])
                    //this is good, because although integer range is used, an id is specified so that the whole input together can be an identifiable
                }
            }
            Section(header: Text("identifiable letter")){
                ForEach(alphaList4){alphaEle in
                    Text(alphaEle.letter)
                    //this is good, because alphaList4 is identifiable
                }
            }
            Section(header: Text("identifiable letter with UUID")){
                ForEach(alphaList5){alphaEle in
                    Text(alphaEle)
                }
            }
        }
        Button(action: {
            alphaList.append("ff")
        }, label: {
            Text("Add for normal list")
        })
        Button(action: {
            alphaList2.append("ff")
        }, label: {
            Text("Add for extract idx")
        })//This button will fail. 
        Button(action: {
            alphaList3.append("ff")
        }, label: {
            Text("Add for extract idx with id")
        })
        Button(action: {
            alphaList4.append(alpha("ff"))
        }, label: {
            Text("Add for identifiable objects")
        })
        Button(action: {
            alphaList5.append("ff")
        }, label: {
            Text("Add for identifiable objects with uuid")
        })
    }
}

struct ListLab_Previews: PreviewProvider {
    static var previews: some View {
        ListLab()
    }
}

总之,在SwiftUI中,输入ForEach的数据集里的元素必须Identifiable,即有独一无二的id属性。如果数据本身没有这样的属性,则需要通过函数的id参数自定义属性。

参考资料

[1]ForEach | Apple Developer Documentation

[2]SwiftUI 基础篇之 ForEach

[3]Documentation (Key path)

[4]Swift 中强大的 Key Paths(键路径)机制趣谈(上)_swift keypath-CSDN博客

[5]Swift - 基础之extension - 简书

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

了解云容器实例云容器实例(Cloud Container Instance)

1.什么是云容器实例&#xff1f; 云容器实例&#xff08;Cloud Container Instance&#xff0c; CCI&#xff09;服务提供 Serverless Container&#xff08;无服务器容器&#xff09;引擎&#xff0c;让您无需创建和管理服务器集群即可直接运行容器。 Serverless是一种架构理念…

【WEB】序列一下

1、 2、反序列化 <?phpclass Polar{public $url polarctf.com;public $ltsystem;public $bls /;function __destruct(){$a $this->lt;$a($this->b);} }$a new Polar(); echo serialize($a); ?>###O:5:"Polar":3:{s:3:"url";s:12:"…

山体滑坡检测系统源码分享

山体滑坡检测检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Computer Vis…

【成品论文】2024年华为杯研赛E题25页高质量成品论文(后续会更新

您的点赞收藏是我继续更新的最大动力&#xff01; 一定要点击如下的卡片链接&#xff0c;那是获取资料的入口&#xff01; 点击链接加入【2024华为杯研赛资料汇总】&#xff1a;https://qm.qq.com/q/Mxv2XNWxUc https://qm.qq.com/q/Mxv2XNWxUc 高速公路应急车道紧急启用模型…

OpenGL使用Glfw框架创建第一个窗体

code #include <iostream> /* glad必须先包含&#xff0c;后包含glfw */ #include "glad/glad.h" #include "glfw/glfw3.h"int main() {// 1 初始化GLFW基本环境glfwInit();// 1.1设置OpenGL主版本、次版本glfwWindowHint(GLFW_CONTEXT_VERSION_MAJ…

C++入门基础知识79(实例)——实例 4【求商及余数】

成长路上不孤单&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a; 【14后&#x1f60a;///C爱好者&#x1f60a;///持续分享所学&#x1f60a;///如有需要欢迎收藏转发///&#x1f60a;】 今日分享关于C 实例 【求商及余数】相关内容&#xf…

基于 Qwen2-1.5B Lora 微调训练医疗问答任务

一、Qwen2 Lora 微调 Qwen是阿里巴巴集团Qwen团队研发的大语言模型和大型多模态模型系列。Qwen2 是 Qwen1.5 的重大升级。无论是语言模型还是多模态模型&#xff0c;均在大规模多语言和多模态数据上进行预训练&#xff0c;并通过高质量数据进行后期微调以贴近人类偏好。Qwen具…

Redisson分布式锁分析,可重入、可续锁(看门狗)

前言 在此说明&#xff0c;本文章不只是讲一些抽象的概念&#xff0c;而是可落地的&#xff0c;在日常工作中基本上进行修改一下便可以使用。书接上回&#xff0c;上篇自研分布式锁的文章使用是一个自己手写的一个分布式锁&#xff0c;按照JUC里面java.util.concurrent.locks.L…

Linux根文件系统构建

直接参考【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.81 本文仅作为个人笔记使用&#xff0c;方便进一步记录自己的实践总结。 Linux“三巨头”已经完成了 2 个了&#xff0c;就剩最后一个 rootfs(根文件系统)了&#xff0c;本章我们就来学习一下根文件系统的组成以及如何构建…

苹果叶片病理分类数据集

苹果叶片病理分类数据集&#xff0c;数据集包括&#xff08;a&#xff09;健康叶片; (b) 苹果链格孢叶斑病&#xff1b; (c) 褐斑病&#xff1b; (d) 蛙叶斑病&#xff1b; (e) 灰斑&#xff1b; (f) 苹果花叶病&#xff1b; (g) 白粉病&#xff1b; (h) 叶片锈病&#xff1b; …

Redis数据结构之哈希表

这里的哈希表说的是value的类型是哈希表 一.相关命令 1.hset key field value 一次可以设置多个 返回值是设置成功的个数 注意&#xff0c;哈希表中的键值对&#xff0c;键是唯一的而值可以重复 所以有下面的结果&#xff1a; key中原来已经有了f1&#xff0c;所以再使用hse…

AI智能跟踪技术核心!

1. 目标检测技术 在视频序列的第一帧中&#xff0c;通过目标检测算法确定要追踪的目标对象的位置和大小。 技术实现&#xff1a;目标检测算法可以基于传统的图像处理技术&#xff0c;如颜色、纹理、形状等特征&#xff0c;也可以基于深度学习方法&#xff0c;如卷积神经网络&…

利用人工智能改变视频智能

人工智能视频分析正在将安全摄像头变成强大的传感器&#xff0c;可以改善您监控站点安全的方式。借助人工智能 (AI)&#xff0c;摄像头可以独立准确地检测威胁&#xff0c;而无需人工不断观看视频。 这并不奇怪——过去几年&#xff0c;这一直是安全行业协会 (SIA) 提出的几大…

Linux命令--05----find 日志

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 find1.语法语法&#xff1a;find 目标目录(路径) <选项> 参数 2.示例3.find 结合 xargs4.案例.* 模糊匹配 绝对路径 find 在 Linux 命令中&#xff0c;fin…

nginx架构篇(三)

文章目录 一、Nginx实现服务器端集群搭建1.1 Nginx与Tomcat部署1. 环境准备(Tomcat)2. 环境准备(Nginx) 1.2. Nginx实现动静分离1.2.1. 需求分析1.2.2. 动静实现步骤 1.3. Nginx实现Tomcat集群搭建1.4. Nginx高可用解决方案1.4.1. Keepalived1.4.2. VRRP介绍1.4.3. 环境搭建环境…

银河麒麟V10系统崩溃后的处理

银河麒麟V10系统崩溃后的处理 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 当银河麒麟桌面操作系统V10崩溃无法启动时&#xff0c;直接使用备份还原工具不可行。此时&#xff0c;应采取以下步骤&#xff1a; 进入救援模式或LiveCD&#x…

基于Java springboot+mybatis 网上商城系统

基于Java springbootmybatis 网上商城系统 一、系统介绍二、功能展示1.主页(客户)2.登陆&#xff08;客户&#xff09;3.注册&#xff08;客户&#xff09;4.购物车(客户)5.我的订单&#xff08;客户&#xff09;6.用户管理&#xff08;管理员&#xff09;7.分类管理&#xff0…

ICM20948 DMP代码详解(35)

接前一篇文章&#xff1a;ICM20948 DMP代码详解&#xff08;34&#xff09; 上一回终于解析完了inv_icm20948_initialize_lower_driver函数&#xff0c;本回回到icm20948_sensor_setup函数&#xff0c;继续往下进行解析。为了便于理解和回顾&#xff0c;再次贴出icm20948_senso…

缓存中间件Redis进阶之路二(快速安装Redis)

一、快速安装Redis “工欲善其事&#xff0c;必先利其器”&#xff0c;在代码实战之前&#xff0c;需要在本地开发环境安装好Redis。对于不同的开发环境&#xff0c;Redis的安装及配置方式也不尽相同&#xff0c;本文将以Windows开发环境为例&#xff0c;介绍Redis的快速安装与…

Kafka 为什么这么快?

Kafka 是一款性能非常优秀的消息队列&#xff0c;每秒处理的消息体量可以达到千万级别。今天来聊一聊 Kafka 高性能背后的技术原理。 1 批量发送 Kafka 收发消息都是批量进行处理的。我们看一下 Kafka 生产者发送消息的代码&#xff1a; private Future<RecordMetadata>…