首页 > 其他分享 >SwiftUI里的ForEach使用的注意事项

SwiftUI里的ForEach使用的注意事项

时间:2024-09-17 17:49:53浏览次数:3  
标签:String Text 元素 SwiftUI ForEach 注意事项 var id

在用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 - 简书

标签:String,Text,元素,SwiftUI,ForEach,注意事项,var,id
From: https://blog.csdn.net/weixin_41640959/article/details/142305305

相关文章

  • ES6 常见坑点及注意事项
    ......
  • MySQL 中的 GROUP BY 和 HAVING 子句:特性、用法与注意事项
    在MySQL数据库的查询操作中,GROUPBY和HAVING子句是非常强大的工具,它们能够帮助我们对数据进行分组和筛选,从而更好地分析和处理数据。今天,我们就来深入了解一下GROUPBY和HAVING子句的特性、用法及注意事项。一、GROUPBY子句的特性与用法特性GROUPBY用于将查询结......
  • ROM修改进阶教程------如何修改固件 线刷转卡刷 卡刷转线刷 操作中的一些注意事项
            在接待各种rom定制化服务中。有很多客户需要各种各样的需求。包括修改rom默认开启usb调试类默认开启开发者选项。修改不锁屏 不休眠跳过开机引导以及一些内置app和可卸载app等等的定制项目。还有很多导出系统导出数据完整恢复类要求。今天给大......
  • 【AD那些事 7 】板子完成!铺铜后!检查的!注意事项! !!板子连线过程中注意事项!!!
    板子连线过程中注意事项:第一,不能距离边缘过近第二,走线不能过长,保证焊盘焊盘间距离短(拒绝太多折线连接)第三,在布线途中旋转器件,确保连接第四,确保GND线可以互相连通如果未连接地线,通过铺铜连接地线时注意事项:1.这样连接是锐角,不能哦  (同一层线不能锐角)   2.铺......
  • PbootCMS怎么安装?注意事项
    安装PbootCMS的过程相对直接,下面是详细的步骤,帮助你完成安装:1.下载PbootCMS程序访问PbootCMS官方网站或其他可信来源下载最新版本的程序包。确保下载适合你服务器环境的版本。2.上传程序包使用FTP客户端(如FileZilla)或其他方式将程序包上传到你的虚拟主机或服务器。上传......
  • 【oj刷题】滑动窗口篇:滑动窗口的应用场景和注意事项
    前言:滑动窗口其实基本原理还是双指针,但在双指针中左右指针可能会有回退操作,而滑动窗口的左右指针只会向前走,不会回退,下面就来讲解一下滑动窗口的概念和具体操作(主要是例题讲解)目录一、什么是滑动窗口?二、滑动窗口的原理三、滑动窗口的算法实现四、滑动窗口的例题讲解......
  • ITU-T测试注意事项具体有哪些?
    ITU-T测试是通信领域中的一个重要环节,它涉及到国际电信联盟(ITU)制定的一系列标准和建议,旨在确保通信设备和系统的互操作性和性能。在进行ITU-T测试时,需要遵循一系列注意事项,以确保测试的准确性和可靠性。以下将详细阐述ITU-T测试的注意事项。一、测试前的准备在进行ITU-T测试之前,首......
  • Kotlin入门实用开发技巧与注意事项
    本文首发于公众号“AntDream”,欢迎微信搜索“AntDream”或扫描文章底部二维码关注,和我一起每天进步一点点Kotlin,这门由JetBrains开发的现代编程语言,自2017年被Google宣布为Android官方开发语言以来,便迅速在开发者社区中流行起来。它以其简洁的语法、空安全的特性......
  • Nacos bootstrap配置文件中关于spring.application.name命名注意事项
    在写配置文件时,把bootstrap文件中的spring.application.name命名时单词之间使用了下划线来连接,结果在启动nacos时报错找不到配置文件一直请求的是本地的nacos,最后发现是在spring.application.name命名时如果使用连接符时必须使用中间的连接符。在官方文档中是这样说的:spring.ap......
  • 大模拟题的注意事项合集
    P2058[NOIP2016普及组]海港坑点:一开始的思路是直接建一个队列,如果队里有这个国家的人那么就不进队,根据时间不断删除队首,查询队列的size。很容易看出这个思路不可行。因为这个时刻队列中有这个国籍的人,下一次就不一定是这一个了那我们记录此时队列中有几个此国籍的人,根据......