iOS开发 SwiftUI 6 :List

目录

List

基本List

基于数据的List

定义数据模型Model

Identifiable协议

示例

拖动排序和删除

注意,预览功能不全

修饰

样式.listStyle

.automatic

.grouped

.inset

.insetGrouped

.plain

去掉列表项分隔线

设置列表项背景色

设置列表背景色

背景色的特殊之处

列表的背景色会占据相邻的安全区,不影响边框


List

List属于比较复杂的视图,功能比较多,问题也比较多。

基本List

基本List类似VStack,每个子项目就是列表的一个条目,具体不限:

Swift 复制代码
 List{
                    Text("123")
                    Image(systemName:"magnifyingglass")
                    Text("123")
                }

效果:

如果是VStack就不会有边框和横线装饰,而且内容是居中的。

基于数据的List

很多情形列表来自数据,还需要根据数据变化自动更新,这就需要使用FofEach来遍历数据生成List的内容。

最简单的情况是用字符串数组:

Swift 复制代码
var items : [String] = ["123","456","123"]

                List{
                    ForEach(items.indices,id: \.self){
                        item in
                        Text(items[item])
                    }
                }

ForEach第一个参数是要迭代的对象集合,第二个参数是用作识别每一条数据的id,如果没有给出,则要迭代的对象必须支持Identifiable。迭代参数是每一个对象。

效果:

注意到我们有两条"123",正确区分了,所以".self"明显不是数据的内容。作为基本数据显示,我们现在并不关心每条数据如何唯一标识,不过既然系统做了如此约束,后面应该是有大用处的。

定义数据模型Model

现代编程语言都喜欢把数据结构称为"Model"并集中存放在一个目录下,但我仍然习惯把结构和功能放在一起(当然不一定是一个文件,但至少放在同一个目录下),在你写的项目足够高大上以前,你的代码也没必要那么高大上。

Identifiable协议

用做基础的List的来源数据(实际上是ForEach的来源数据),唯一的要求是符合Identifiable协议。这个协议唯一的要求要有id属性来做唯一标识。如果不明确定义id属性,Identifiable可以默认生成一个,默认生成的其实就是对象的地址(指针?嗯)。

示例

Swift 复制代码
struct Data: Identifiable {
    var id: UUID = UUID()
    var i: Int
    var name: String
}

//in view
//注意此时@State是不需要的,因为还不涉及到双向绑定
@State var datas: [Data] = [
        Data(i:1,name: "magnifyingglass"), Data(i:2,name: "globe.americas"),
        Data(i:3,name: "sun.rain.circle"),
    ]

//in body
                List {
                    ForEach(datas) {
                        item in
                        HStack{
                            Text("\(item.i)")
                            Image(systemName: item.name)
                        }
                    }
                }

效果:

UUID是全球唯一标识符,生成方式可以保证同一个程序同一次运行生成的肯定是唯一的,不刻意捣鬼的话不同计算机生成的发生碰撞的可能性也是可以忽略的。

注意我们在ForEach里根据数据内容生成了Text和Image,用HStack组合起来。如果不组合起来,对List来说,就会生成六条数据,这样,在我们后面操作移动、删除的时候,传递的List的序号和数据如何对应就成了问题。

拖动排序和删除

支持拖动排序和删除的并不是List,而是ForEach!所以我们需要给ForEach加上排序和删除动作,而不是给List加:

Swift 复制代码
                List {
                    ForEach(datas) {
                        item in
                        HStack{
                            Text("\(item.i)")
                            Image(systemName: item.name)
                        }
                    }
                    .onMove(perform: { IndexSet, Int in
                        info = "\(IndexSet.first ?? -1) \(IndexSet.last ?? -1) to:\(Int)"
                        datas.move(fromOffsets: IndexSet, toOffset: Int)
                    })
                    .onDelete(perform: { IndexSet in
                        datas.remove(atOffsets: IndexSet)
                    })
                }

两个动作也可以写成函数:

Swift 复制代码
    func moveItem(form source: IndexSet, to destination: Int) 
    {
        
    }
    func deleteItem(at offsets:IndexSet)
    {
        
    }

里面的代码差不多,注意参数名不一样,直接写的代码用的参数名就是参数类型(写代码的时候双击perform后面的提示自动生成的)。

info是我额外增加的用来显示参数的视图:

Swift 复制代码
    @State var info : String = "info"

//in view
VStack
            {
                List {
                    ......
                }
                
                Text(info)
            }

根据显示的内容,可以明白:索引值是基于0的,拖放到第一个时插入位置是0,拖放到最后一个时插入位置时最后一个+1(三条数据,0,1,2,把第一条拖到最后,插入位置是3)。

删除是滑动删除:

注意,预览功能不全

我的电脑上预览界面无法执行拖动,实体机上正常。

修饰

样式.listStyle

注意这是List的修饰,不是ForEach的。

.automatic

默认样式

.grouped

没有圆角外包了,但上下空白还是有的。

.inset

没有上下空白了(红色是我设置的总VStack的背景色)。

.insetGrouped

这......好像又回到默认了啊。

.plain

注意关键区别,不仅没有圆角边界,连下面多余的空白都没有了。

但是,不要以为List变小了!你看到的效果仅仅是因为List没有绘制背景,从而把外部的红色背景透过来了!加上背景色看看(.background(.blue)):

更晕了,原来List的背景色和数据项的背景色是无关的。而且,这样设置背景色的方法仅适用于.plain,对别的样式是无效的。

.sidebar

看起来又回到默认了。

去掉列表项分隔线

分隔线属于ForEach而不是List,所以要给ForEach加:

Swift 复制代码
                    ForEach(datas) {
                        。。。。。。
                    }
                    .onMove(perform: { IndexSet, Int in
                        。。。。。。
                    })
                    .onDelete(perform: { IndexSet in
                        。。。。。。
                    })
                    .listRowSeparator(.hidden)//去掉分隔线

设置列表项背景色

列表项背景色也属于ForEach。

我们可以看到默认的列表项背景色是比List的背景色要亮一些的,我们可以设置为与背景色一致:

Swift 复制代码
//ForEach的修饰
.listRowBackground(Color.clear)

当然也可以指定颜色:

Swift 复制代码
.listRowBackground(Color.green)

设置列表背景色

前面已经说了,直接设置.background仅对.plain风格有效,其他风格必须配合.scrollContentBackground(.hidden)才行:

Swift 复制代码
            .scrollContentBackground(.hidden)
            .background(.blue)

效果:

背景色的特殊之处

列表的背景色会占据相邻的安全区,不影响边框

不需要设置,顶部的那个列表直接占据了顶部安全区:

Swift 复制代码
    var body: some View {
            VStack {
                List {
                    Text("123")
                    Image(systemName: "magnifyingglass")
                    Text("123")
                }
                .scrollContentBackground(.hidden)  //取消默认背景才能让background生效
                .background(.blue)  //除了.listStyle(.plain)都必须有上一句配合
                List {
                    ForEach(items.indices, id: \.self) {
                        item in
                        Text(items[item])
                    }
                }
                List {
                    ForEach(datas) {
                        。。。。。。
                    }
                    .onMove(perform: { IndexSet, Int in
                        。。。。。。
                    })
                    .onDelete(perform: { IndexSet in
                        。。。。。。
                    })
                    .listRowSeparator(.hidden)
                    .listRowBackground(Color.green)
                }
                .scrollContentBackground(.hidden)  //取消默认背景才能让background生效
                .background(.blue)  //除了.listStyle(.plain)都必须有上一句配合

                Text(info)
            }
            .border(.black)
            .background(.red)
    }

代码中有三个列表,效果:

有点困惑,简单说,VStack和List的背景色都会占据相邻的安全区,Text不会。因此上面的代码中顶部List的背景色铺到了顶部安全区,VStack的背景色则铺满了底部。但它们的边界都不包含安全区。如果在第一个List之前加一个Text,则VStack占据所有的安全区。

所以特殊性仅仅局限于背景色。

相关推荐
游戏开发爱好者88 小时前
带签名时间戳接口的重放与压力测试实战,用动态值在发送前现算签名
网络协议·计算机网络·网络安全·ios·adb·https·压力测试
2501_9160074721 小时前
申请 iOS 推送证书并配置 APNs 群发推送教程
android·ios·小程序·https·uni-app·iphone·webview
weixin_403810131 天前
iOS自动化脚本怎么避免风控?无屏幕镜像截图原理详解
ios·自动化·跨境电商·苹果群控·苹果投屏·多媒体运营
东坡肘子1 天前
越来越慢的 App Review,拦住了谁? -- 肘子的 Swift 周报 #149
人工智能·swiftui·swift
wangruofeng1 天前
3 步把 MacBook 变成 7×24 远程主机,人在外面随时接管桌面
ios·agent·mac
weixin_403810131 天前
iOS免越狱自动化蓝牙HID方案:不装代理也能跑脚本
运维·ios·自动化·跨境电商·ai智能体·ios投屏·苹果投屏
GitLqr1 天前
Flutter 3.47 与 Dart 3.13:你必须了解的迁移指南
flutter·ios·dart
冯汉栩2 天前
Swift Control View,Label渐变颜色(源码)
开发语言·ios·swift
jike_20262 天前
iPhone日历查看会议纪要APP推荐:按日期查找历史录音更方便
ios·语音识别·iphone
冯汉栩2 天前
OC 技术 App上存蒲公英教程
ios