SwiftUI中List的liststyle样式及使用详解添加、移动、删除、自定义滑动

SwiftUI中的List可是个好东西,它用于显示可滚动列表的视图容器,类似于UITableView。在List中可以显示静态或动态的数据,并支持垂直滚动。List是一个数据驱动的视图,当数据发生变化时,列表会自动更新。针对List,我们还可以进行添加、移动、删除以及滑动等功能。示例代码:

//
//  RouterView.swift
//  SwiftBook
//
//  Created by song on 2024/7/4.
//

import SwiftUI

struct RouterView: View {
    @State var monthes = [
        "January",
        "February",
        "March",
        "April",
        "May",
        "June",
        "July",
        "August",
        "September",
        "October",
        "November",
        "December"
    ]

    var body: some View {
        List(monthes, id: \.self) { m in
            Text(m)
        }
    }
}

#Preview {
    RouterView()
}

显示静态数据

显示静态的数据还是比较简单的,比如要显示12个月份,代码中,直接在List组件里面放置Text组件即可,不过如果同类型数据过多,还是采用动态数组的方式去显示。

显示动态数据

很多的时候采用的都是动态数组显示的方式,比如通过网络请求回来的数据,再通过List显示出来等等:

上面的代码简化了很多,我们将months数据作为第一个参数,传给了List,而List的第二个参数id,则表示months数据中的元素采用自身作为唯一的标识符。如果数组中的两个或多个元素不是唯一的,这可能会导致问题。

如果不用字符串数据,而是该用了实例对象数组,则可以将实例对象的结构实现Identifiable协议,该协议需要实现一个id属性,如果将一个实现了Identifiable协议的实例对象数组传给List,则不需要指定id参数了。比如下面的代码示例:

样式设置

默认的List是有背景色,Light模式下是白色,Dark模式下是灰色,比如下面这个代码: 

背景色

只需要添加一行代码即可,将其修饰在List闭包内部的组件上,即修饰在Text组件上。如果直接修饰在List组件上,则没有任何效果。 

内间距

另外上面的代码中,Text的宽度没有完全和cell的宽度对齐,两边还有一些间距,有的时候是不需要这个间距的,设置下面代码即可,同样是要修饰在List闭包内的组件上:

分割线颜色与隐藏

设置分割线以及是否显示需要将下面两个修饰符作用在List闭包内的组件上。

// 隐藏分割线,默认是显示的。
.listRowSeparator(.hidden)
// 设置分割线颜色。
.listRowSeparatorTint(.red)

设置Cell之间的间距

默认cell之间是采用分割线进行区分的,如果设置间距,则分割线直接就隐藏了。该修饰符需要作用在List组件上,效果如下图,设置了10个间距:

List样式

设置List样式可以通过在括号内需要传入对应的style参数。listStyle修饰符需要作用在List组件上。

automatic:默认列表样式,根据设备和环境自动选择合适的列表样式。
plain:普通列表样式。
grouped:分组列表样式。
insetGrouped:缩进分组列表样式。
sidebar:侧边栏列表样式。
inset:缩进列表样式。
以上样式的效果图如下,有些样式不太明显,结合其他的修饰符效果可能更好。

分组显示(Section、 Header、 Footer)

List组件要想实现分组的功能,很简单,在List组件中使用Section组件即可。Section组件支持Header和Footer功能,同时Header和Footer也支持直接设置Title和自定义。

比如下面直接设置Section title的示例,直接给Title一个字符串,在content闭包内通过ForEach循环添加要显示的组件,当然也可以不用循环,而是静态数据。

下面是自定义Header和Footer的示例,Header中横向显示了Image和Text组件,Footer中添加了一个Text,效果如下:

这里额外说一下List和ForEach循环的结合使用的场景:

如果只是单纯的显示一些静态数据,或者一个数据数据,只用List组件即可。
如果要显示数组数据,可以单独使用List组件,将数组数据传给List,也可以在List组件中添加ForEach组件,通过ForEach循环遍历数组数据。

  var body: some View {
    List {
      ForEach(fruits, id: \.self) { fruit in
        Text(fruit.capitalized)
      }
    }
  }

如果即有静态数据又有动态数据要显示,则需List和ForEach组合使用:

上面的代码稍微有些多,主要为了视觉效果更好一些。从效果图中可以看出是人为设置了两个Header,采用了Text组件,并设置了相关的属性修饰符。代码中不难看出List组件里面采用了ForEach,同时也单独设置了Text组件。
另外需要提一下,我们给ForEach设置了前景颜色,这个前景颜色会作用在ForEach中的每个Text上。还有给Group组件设置了List cell的样式,那么这个样式也会作用在List中的每个子组件上,这样做代码会更加简洁明了。

添加和删除元素
只能在List的动态部分添加和删除元素,而静态元素不能在应用程序运行时添加或删除。因此,我们的列表需要完全动态,或者至少包含一个动态部分,以便我们能够添加或删除元素。

为了有添加和删除的入口,我们添加了导航栏,并设置了导航栏的toolbar,导航栏的设置这里不过多阐述。

添加元素
在导航栏的右侧添加了一个添加元素的按钮,点击后在List要展示的数据中添加元素,此时也要求该数据必须被@State包裹修饰,以便其修改后触发UI刷新。具体代码见下面效果图。

删除元素
在导航栏左侧添加了一个EditButton,点击后将整个List设置为编辑模式,并在每个cell的左侧提供一个红色的删除按钮。这个过程主要是由系统自动的。不过前提是要对List添加至少一个可操作的方法,比如onDelete,onMove等,否则点击了EditButton,List也没有任何变化。
另外对单个cell进行左滑操作也可以删除元素。

为了实现删除效果,需要在ForEach上添加onDelete方法,如下:

.onDelete(perform: { indexSet in
                        
})

这个onDelete方法返回一个IndexSet类型的值,我们可以在这个闭包内进行数据删除,不过为了UI逻辑和业务逻辑低耦合,采用单独抽出来一个方法处理删除逻辑,如下:

func deleteItem(indexSet: IndexSet) {
  fruits.remove(atOffsets: indexSet)
}

在deleteItem方法里面,fruits数组调用remove(atOffsets:)方法即可。

因为onDelete方法返回一个IndexSet类型的值,所以抽出来的方法同样接受这样类型的值。最终只要在ForEach上调用下面代码即可,不用额外写传递的参数:

ForEach(fruits, id: \.self) { fruit in
	Text(fruit.capitalized)
}
.onDelete(perform: deleteItem)

最终代码及效果如下图:

移动功能

移动功能还是比较普遍的,基于上面的代码,只需要在ForEach上添加下面的方法即可。

.onMove(perform: { indices, newOffset in

})

这个方法返回两个参数,第一个为IndexSet类型的值,第二个为Int类型的值。同onDelete一样,我们单独抽出来一个方法,同样接收这两个参数。

func moveItem(indexSet: IndexSet, offSet: Int) {
  fruits.move(fromOffsets: indexSet, toOffset: offSet)
}

在moveItem方法中,fruits数据调用move(fromOffsets:, toOffset:)方法即可。最后在ForEach上添加该方法:

ForEach(fruits, id: \.self) { fruit in
	Text(fruit.capitalized)
}
.onMove(perform: moveItem)

实现了上面的代码后,我们可以在List的编辑模式下拖动cell移动,编辑模式下每个cell右侧有三个横杠的图标,拖动即可移动。另外非编辑模式下长按cell后也可以进行拖动,代码及效果图如下:

 

自定义左滑功能

SwiftUI的swipeActions()修饰符允许你添加一个或多个滑动动作按钮到你的列表行,可选地控制他们属于哪一边,以及他们是否应该被触发使用一个完整的滑动。
先看下代码及效果图: 

swipeActions()方法有三个参数,第一个edge决定操作按钮放哪边;第二个allowsFullSwipe决定完全滑动是否自动执行第一个操作,默认值为true;第三个即是内容闭包了。

关于样式,只能通过tint设置背景色,如果不设置,系统默认是灰色的。
SwiftUI底层还是很聪明的,比如第一个Button,我们用Text显示内容,只有文字,那么系统就显示了Add文字,第二个和第三个用Label显示,有文字和图片,但是系统聪明的只显示了图片。

对于想要拥有删除功能的按钮,应该使用Button(role: .destructive),而不是仅仅指定一个红色背景色,比如上面第三个按钮,不需要有任何删除的逻辑,聪明的底层帮我们实现了。

Button(role: .destructive) {

} label: {
  Label("Delete", systemImage: "trash.fill")
}

SwiftUI中的List确实是一个非常好用的组件,包含的功能也比较多,大多数的App界面也都是由导航栏和List组成的。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

PyMuPDF 操作手册 - 09 API - Page属性方法和简短说明

文章目录 一、Page属性方法和简短说明一、Page属性方法和简短说明 https://pymupdf.readthedocs.io/en/latest/page.html Method/Attribute属性方法Short Description简短说明Page.add_caret_annot()仅限 PDF:添加插入符号注释Page.add_circle_annot()仅限 PDF:添加圆圈…

前端面试题7(单点登录)

如何实现单点登录 单点登录(Single Sign-On,简称SSO)是一种允许用户在多个应用系统中只需登录一次,就可以访问所有相互信任的应用系统的认证技术。实现前端单点登录主要依赖于后端的支持和一些特定的协议,如OAuth、Ope…

Postman使用教程

传统接口风格 RESTful风格 使用Postman完成测试用例目标: Postman教程 (1)准备工作,下载Postman新建 (2)登录接口调试-获取验证码 (3)登录接口调试-登录 (4)…

python库(2):Passlib库

1 Passlib简介 Passlib库就是一个强大的工具,专门用于密码的安全存储和验证。本文将介绍Passlib库的基本概念、功能和使用方法,帮助更好地理解和应用密码安全技术。 Passlib是一个用于密码加密、哈希和验证的Python库,它提供了多种密码哈希…

STM32 HAL库实现硬件IIC通信

文章目录 一. 前言二. 关于IIC通信三. IIC通信过程四. STM32实现硬件IIC通信五. 关于硬件IIC的Bug 一. 前言 最近正在DIY一款智能电池,需要使用STM32F030F4P6和TI的电池管理芯片BQ40Z50进行SMBUS通信。SMBUS本质上就是IIC通信,项目用到STM32CubeMXHAL库…

算法库应用-顺序串(串比较)

学习贺利坚老师博客 数据结构例程——串的顺序存储应用_使用顺序串存储身份证号-CSDN博客 本人详细解析博客 串的顺序存储结构应用_(1)假设串采用顺序串存储,设计一个算法程序,按顺序比较两个串s和t的大小。请-CSDN博客 版本日志 V1.0: 利用顺序串, 进行简单的判断比较, 也算是…

JavaScript中闭包的理解

闭包(Closure)概念:一个函数对周围状态的引用捆绑在一起,内层函数中访问到其外层函数的作用域。简单来说;闭包内层函数引用外层函数的变量,如下图: 外层在使用一个函数包裹住闭包是对变量的保护&#xff0c…

Linux--V4L2摄像头驱动框架及UVC浅析

一、前言 对于一个usb摄像头,它的内核驱动源码位于/drivers/media/usb/uvc/ 核心层:V4L2_dev.c文件 硬件相关层: uvc_driver.c文件 本篇记录基于对6.8.8.8内核下vivid-core.c文件(虚拟视频驱动程序)的分析&#xff…

【数据库】仓库管理数据库(练习样例)

某连锁超市需要设计实现一个仓库管理系统,要求每个仓库可以有多名仓库管理员,每个仓库管理员只负责管理一个仓库,同时每个仓库都配备了一名仓库主管;不同的仓库存放的是不同类型的货品,每种货品只存放在固定的仓库中&a…

Os-hackNos

下载地址 https://download.vulnhub.com/hacknos/Os-hackNos-1.ova 环境配置如果出现,扫描不到IP的情况,可以尝试vulnhub靶机检测不到IP地址解决办法_vulnhub靶机扫描不到ip-CSDN博客 信息收集 确定靶机地址: 探测到存活主机192.168.111.…

modelscope可控细节的长文档摘要

modelscope可控细节的长文档摘要尝试 本文的想法来自今年OpenAI cookbook的一篇实践:summarizing_long_documents,目标是演示如何以可控的细节程度总结大型文档。 如果我们想让大语言模型总结一份长文档(例如 10k 或更多tokens)&…

【MySQL】 NDB 集群概述

MySQL NDB(Network Database)是MySQL的一个存储引擎,也称为NDB Cluster存储引擎。它主要用于构建高可用性、高可扩展性和高性能的分布式数据库集群。NDB Cluster是MySQL的一个特殊版本,专门设计用于处理大规模的分布式数据存储和处…

【MySQL】MySQL 9.0悄悄的来了

MySQL 9.0.0 中的变化 MySQL 9.0 中的新功能 JavaScript 存储程序 MySQL 企业版现在支持用 JavaScript 编写的存储程序,例如使用 CREATE FUNCTION下面显示的语句和 JavaScript 代码创建的这个简单示例: CREATE FUNCTION gcd(a INT, b INT) RETURNS …

SpringBoot-第一天学习

SpringBoot介绍-约定大于配置 SpringBoot是在Spring4.0基础上开发的,不是替代Spring的解决方案,而是和Spring框架结合并进一步简化Spring搭建和开发过程的。 如何简化?就是通过提供默认配置等方式让我们更容易,集成了大量常用的…

泛微开发修炼之旅--29用计划任务定时发送邮件提醒

文章链接:29用计划任务定时发送邮件提醒

嵌入式Linux系统编程 — 6.7 实时信号

目录 1 什么是实时信号 2 sigqueue函数 3 sigpending()函数 1 什么是实时信号 等待信号集只是一个掩码,它并不追踪信号的发生次数。这意味着,如果相同的信号在被阻塞的状态下多次产生,它只会在信号集中被记录一次,并且在信号集…

Django文档简化版——Django快速入门——创建一个基本的投票应用程序

Django快速入门——创建一个基本的投票应用程序 准备工作1、创建虚拟环境2、安装django 1、请求和响应(1)创建项目(2)用于开发的简易服务器(3)创建投票应用(4)编写第一个视图1、编写…

FreeRTOS的任务间通信

文章目录 4 FreeRTOS任务间通信4.1 队列4.1.1 队列的使用4.1.2 队列的创建,删除,复位4.1.3 队列的发送,接收,查询 4.2 邮箱(mailbox)4.2.1 任务中读写邮箱4.2.2 中断中读写邮箱 4.3 队列集4.3.1 队列集的创…

linux19:程序替换

一&#xff1a;最简单的看看程序替换是什么样的&#xff08;单个进程版&#xff09; 1 #include<stdio.h>2 #include<unistd.h>3 #include<stdlib.h>4 int main()5 {6 printf("Before : I am a process , myPid:%d,myPPid:%d\n",getpid(),getpp…

【Ubuntu】详细说说Parallels DeskTop安装和使用Ubuntu系统

希望文章能给到你启发和灵感~ 如果觉得文章对你有帮助的话,点赞 + 关注+ 收藏 支持一下博主吧~ 阅读指南 开篇说明一、基础环境说明1.1 硬件环境1.2 软件环境二、Ubuntu系统的使用2.1 系统的下载2.2 系统的安装2.3 安装桌面版(可选)2.3.1 安装/更新apt2.3.2 安装桌面版2.3…