实战教程·元宇宙来了,准备好你的电子名片了吗?(二)
theme: smartblue
前提回顾
在上一章中,我们创建了CardView构件,使用了构件编程方式搭建视图。使用构件的好处是当我们要整体修改构件内容时,就可以只修改构件的结构体,而无需每个卡片视图都修改。
示例,在上一章中我们设置了平台图标图片,但由于平台图标样式的不统一性,看起来有些不协调。这时候,我们可以给CardView构件中的Image控件设置为圆形,让其更加美观些,如下图所示:
对于搭建好的构件,只需要修改CardView构件本身,就可以在引用此构件的所有地方统一修改其样式。
是不是很方便?接下来让我们继续完成下面的内容。
结构化编程:搭建数据模型
使用构件创建身份卡片视图后,我们发现如果每次要新增一个卡片,就需要在卡片视图中需要复制CardView构件并赋值。当我们需要创建很多身份卡片时,就会需要很多一样的代码,这不够优雅。
有没有办法像搭建CardView构件一样,只搭建一个结构然后填充数据,让结构自动“遍历”出内容?
SwiftUI声明式语言中,可以使用List列表组件循环遍历内容,在此之前我们需要定义好数据模型。我们创建一个新的Swift文件,命名为Model,如下图所示:
Model文件作为我们搭建数据模型的文件,定义数据需要声明的变量。首先我们引用SwiftUI,如下代码所示:
import SwiftUI
接下来搭建数据模型,如下代码所示:
struct Model: Identifiable {
var id = UUID()
var platformIcon: String
var title: String
var platformName: String
var indexURL: String
}
数据模型可以使用Class类或者Struct结构体声明,这里我们使用Struct结构体声明了一个数据模型Model,遵循Identifiable协议。“Identifiable可识别”协议可以通过id区分不同的数据,当我们创建的数据中有两条一模一样的数据时,由于“Identifiable可识别”协议,系统将赋予不同的id,实现呈现两条数据内容的效果。
使用Identifiable协议除了声明需要的参数变量外,还需要在数据模型中声明一个id,并复制为UUID。该数据模型中的需要使用的变量参数,我们可以直接使用之前在CardView身份卡片构件声明的参数。
声明好参数后,我们为展示数据,可以创建一个数据数组作为示例数据,如下代码所示:
``` // MARK: 示例数据
var models = [ Model(platformIcon: "icon_juejin", title: "移动端签约作者", platformName: "稀土掘金技术社区", indexURL: "http://juejin.cn/user/3897092103223517"), Model(platformIcon: "icon_csdn", title: "iOS创作者", platformName: "CSDN博客", indexURL: ""), Model(platformIcon: "icon_aliyun", title: "专家博主", platformName: "阿里云社区", indexURL: ""), Model(platformIcon: "icon_huaweiyun", title: "专家博主", platformName: "华为云社区", indexURL: ""), ] ```
如此便完成了数据模型的搭建。
卡片视图:List列表和ForEach循环
我们回到ContentView视图中,删除原有的身份卡片视图的代码。卡片视图我们可以看作一个列表,每张身份卡片就是一行行呈现的数据,列表的数据源则来源于上述创建好的数组models,如下图所示:
List {
ForEach(models) { item in
CardView(platformIcon: item.platformIcon, title: item.title, platformName: item.platformName, indexURL: item.indexURL)
}
.listRowBackground(Color.clear)
.listRowSeparator(.hidden)
}
上述代码中,我们使用List列表和ForEach循环结构,遍历models数组中定义好的数据内容,并在调用CardView身份卡片构件的时候赋值为循环后item的一条条数据。
由于演示的iPhonex模拟器,因此为了呈现更好的列表样式效果,使用listRowBackground列表项背景修饰符,将列表背景颜色修饰为透明。并使用listRowSeparator列表分割线修饰符,隐藏List自带的分割线。完成这些操作后后,就可以完成展示身份卡片构件的样式内容。
在模拟器中预览的效果中,身份卡片左右边距太宽了,我们可以直接修改CardView构件的代码,去掉最后的padding边距,如下图所示:
导航菜单:标题和新建卡片入口
卡片视图完善之后,我们来搭建顶部导航菜单。SwiftUI提供了NavigationView导航视图控件方便我们快速搭建应用的顶部导航菜单。从视图层级来看,顶部导航菜单是“包裹”整个页面视图的,因此NavigationView导航视图控件需要在ContentView的body视图最外层,如下代码所示:
NavigationStack {
//视图内容
.navigationBarTitle("文如秋雨",displayMode: .inline)
}
在iOS16版本,使用NavigationStack导航视图控件搭建顶部导航菜单,iOS16版本以下的机型使用NavigationView导航。
搭建好顶部导航栏后,我们创建一个新的按钮,作为创建身份卡片的入口。可以使用navigationBarItems导航栏按钮修饰符创建导航菜单上的按钮入口,不过在此之前,我们需要搭建好按钮的视图,再将按钮赋予navigationBarItems导航栏按钮修饰符。如下代码所示:
``` func addBtn() -> some View { Button(action: {
}) {
Image(systemName: "plus.circle.fill")
.font(.system(size: 17))
.foregroundColor(.blue)
}
}
```
上述代码中,我们完成了一个按钮视图addBtn。一般在编程过程中,若视图中除样式代码外还有交互操作,通常会将其抽离出来作为一个单独的视图进行构建,然后再在修饰符或者其他视图中使用,这是SwiftUI开发中经常会遇到的事情。
完成按钮视图的构建后,我们将按钮视图加到顶部导航菜单中,如下代码所示:
.navigationBarItems(trailing: addBtn())
模态弹窗:打开一个新页面
个人主页部分基本已经完成了,下面我们来完成点击顶部导航菜单的“添加”按钮,打开“新建身份卡”页面。
首先我们先创建一个新的SwiftUI文件,命名为NewView,如下图所示:
回到ContentView视图中,使用Sheet模态弹窗控件,创建打开弹窗的样式,如下代码所示:
.sheet(isPresented:Is Presented) {
Content
}
上述代码中,我们使用sheet修饰符控件搭建打开模态弹窗效果,但需要使用sheet修饰符控件,需要提前声明好触发打开动作的变量,如下代码所示:
@State var showNewView:Bool = false
并将声明好的触发变量绑定到sheet修饰符控件中,以及设置Content跳转的页面目标为NewView。如下代码所示:
.sheet(isPresented: $showNewView) {
NewView()
}
最后在点击addBtn按钮视图时,切换showNewView打开视图的变量状态,便可实现点击“添加按钮”打开模态弹窗,如下代码所示:
self.showNewView.toggle()
关闭弹窗的方法也很类似,我们来到NewView视图,给NewView视图的内容添加顶部导航菜单,并也创建一个关闭弹窗的按钮,如下代码所示:
``` struct NewView: View { var body: some View { NavigationStack { Text("Hello, World!") .navigationBarTitle("添加身份卡", displayMode: .inline) .navigationBarItems(trailing: closeBtn()) } }
func closeBtn() -> some View {
Button(action: {
}) {
Image(systemName: "xmark.circle.fill")
.font(.system(size: 17))
.foregroundColor(.gray)
}
}
} ```
上述代码中,我们使用的方法和个人主页的内容基本一致,使用NavigationStack导航菜单控件创建导航菜单,并使用navigationBarTitle顶部导航标题控件创建标题。单独创建关闭按钮视图closeBtn,并将它赋予导航菜单中的navigationBarItems导航菜单栏目修饰符,以创建导航菜单上的按钮。
关闭弹窗:双向绑定或全局变量
实现打开模态弹窗后,我们来实现关闭模态弹窗,下面我们来介绍两种关闭模态弹窗的方法:双向绑定、全局变量。
由于我们在ContentView个人主页声明了一个变量showNewView来打开模态弹窗,当点击addBtn按钮时更新其状态。因此我们也可以在NewView页面中也声明一个双向绑定的变量showNewView,并在点击closeBtn时更新其状态,实现关闭弹窗的交互。
首先先声明一个双向绑定的变量showNewView如下图所示:
@Binding var showNewView:Bool
然后在点击closeBtn关闭按钮时更新其状态,如下代码所示:
self.showNewView.toggle()
由于使用@Binding声明变量,因此在NewView视图中声明的变量缺少赋值,将可能导致无法预览,因此我们在预览时需要给NewView视图赋予默认值,如下代码所示:
NewView(showNewView: .constant(false))
建立双向绑定的视图,在所有页面也都需要进行@Binding声明变量的绑定,因此我们回到ContentView视图中,绑定showNewView变量,如下代码所示:
NewView(showNewView: $showNewView)
完成后,可以在模拟器中操作打开模态弹窗和关闭模态弹窗的交互。
另一种关闭模态弹窗的方法是声明一个全局变量,实现关闭弹窗的效果,如下代码所示:
``` //声明环境变量 @Environment(.presentationMode) var presentationMode
//调用 self.presentationMode.wrappedValue.dismiss() ```
上述代码中,我们声明了一个全局变量presentationMode,在点击closeBtn关闭按钮的时候调用presentationMode全局变量的wrappedValue.dismiss方法实现关闭弹窗效果。
两种方式在实际开发过程中均有使用,除模态弹窗外,在页面之间使用入栈出栈的方式进行切换页面也经常会用到。
在模态弹窗交互逻辑中更经常使用全局变量关闭弹窗的方式,无需进行双向绑定已经给视图添加默认值,也就无需示例在其他页面缺少绑定参数的麻烦。
本章小结
在本章中,我们主要实现了使用数据模型和List列表来构建身份卡片视图的内容,在减少代码量的同时,其实也是为了之后对身份卡片进行新增、编辑、删除做前期准备。
在SwiftUI中,复杂的具有相似结构的界面设计,通常会使用List列表和ForEach来实现,常见的类似Todo待办事项App中的卡片列表、信息流App的信息列表、短视频平台的视频封面卡片视图等等。
另外我们还学习了使用NavigationStack导航菜单控件(iOS16版本及以上机型使用,低于此版本机型使用NavigationView,Apple经常会在新控件完善后弃用旧控件,这点有点烦)及其标题、导航按钮修饰符的使用,并进一步了解了使用sheet模态弹窗修饰符打开弹窗。关闭弹窗部分,我们介绍了两种方式实现关闭弹窗,并简单介绍了其使用特点。
在下面的章节中,我们将继续完成“添加身份卡”页面的相关操作,请保持期待吧~
版权声明
本文为稀土掘金技术社区首发签约文章,14天内禁止转载,14天后未获授权禁止转载,侵权必究!
- 实战教程·元宇宙来了,准备好你的电子名片了吗?(一)
- 实战教程·元宇宙来了,准备好你的电子名片了吗?(八)
- 实战教程·什么年代了还在敲传统木鱼?(二)
- 实战教程·元宇宙来了,准备好你的电子名片了吗?(七)
- 实战教程·元宇宙来了,准备好你的电子名片了吗?(六)
- 实战教程·元宇宙来了,准备好你的电子名片了吗?(五)
- 实战教程·元宇宙来了,准备好你的电子名片了吗?(四)
- 实战教程·元宇宙来了,准备好你的电子名片了吗?(三)
- 实战教程·元宇宙来了,准备好你的电子名片了吗?(二)
- 实战教程·什么年代了还在敲传统木鱼?(一)
- 技术下午茶:产品经理是如何工作的?如何才算一份好的需求文档?如何设计一个简单的列表,它应该具备哪些基本功能?
- 发布&选择发布,使用SwiftUI搭建一个新建发布弹窗(上)
- 发布&选择发布,使用SwiftUI搭建一个新建发布弹窗(下)
- 使用SwiftUI搭建一个风筝摇摆动画,实现放风筝的梦想~
- SwiftUI100天:使用SwiftUI搭建一个计时器App
- 实战编程·使用SwiftUI从0到1完成一款iOS笔记App(三)
- 初识MVVM·关于启动页、引导页、登录页的设计细节和交互逻辑
- 谁说程序员不懂浪漫,教你使用SwiftUI搭建一个电子相册送给她吧~
- 实战编程·刻在男人DNA里的浪漫,空气投篮(二)
- 实战编程·使用SwiftUI从0到1完成一款iOS笔记App(四)