第四节 创建列表
接上一篇,我们已经完成了单个Cell的实现,接下来我们直接使用List View(类似UITableView)来完成列表。
参考我们之前使用Stack,我们在单个Cell的外面添加一个List View试试
struct ContentView: View {
var body: some View {
List {
HStack {
Image("埃及-金字塔-预览")
VStack(alignment: .leading) {
Text("金字塔")
Text("埃及")
.font(.subheadline)
.foregroundColor(.secondary)
}
}
}
}
}
可以看到效果已经出来了,已经看到列表的雏形了。
由于我们目前只有1个数据,所以只展示了一行,我们可以在列表中添加多个重复的数据
除了手动写代码添加List View之外,我们还可以这样操作:按住command选中HStack,点击鼠标左键,然后选择“Embed in List”,Xcode就可以自动为我们添加List的代码,并且可以看到设置数据的代码
struct ContentView: View {
var body: some View {
List(0 ..< 5) { item in
Image("埃及-金字塔-预览")
VStack(alignment: .leading) {
Text("金字塔")
Text("埃及")
.font(.subheadline)
.foregroundColor(.secondary)
}
}
}
}
第五节 设置List数据
在这之前我们一直是使用固定的单个数据,如果要使用列表,就必须有数据源(DataSource)
这里我们创建一个新的文件 Scenery.swift,用来设置数据,快捷键 command+N 选择 Swift File
我们首先创建一个结构体,并预设好我们需要的属性的名称
struct Scenery : Identifiable {
var id: Int
var name: String
var thumbnailName: String
var imageName: String
var location: String
}
这里的 Identifiable 需要注意一下,它是为类或值类型提供稳定的身份的协议。可以理解为这个类对象的唯一标识符,我们在使用数据的时候,需要找到对应的数据,那么实现了这个Identifiable协议后,系统就会通过它的唯一id找到它。
除了id之外,其他几个数据都非常好理解,它们分别是:名称、缩略图名称、图片名称、地址。
在定义完属性后,我们就开始输入数据了
定义一个 sceneries 数组,输入“Scenery(” 这个时候你会发现Xcode会自动生成一个初始化方法,我们以此填入数据
let sceneries = [
Scenery(id: 1001, name: "金字塔", thumbnailName: "埃及-金字塔-预览", imageName: "埃及-金字塔-黄昏", location: "埃及")
]
知识补充:在Xcode中添加图片素材非常简单,只需要将图片全选后,拖拽到Assets.xcassets这个文件目录下就可以了,如下图所示
OK,到这里我们就完成了数据的创建,接下来我们将刚才的数据放到List View里面试试
在List View中替换原来写死的数据
struct ContentView: View {
var body: some View {
List(sceneries) { item in
Image(item.thumbnailName)
VStack(alignment: .leading) {
Text(item.name)
Text(item.location)
.font(.subheadline)
.foregroundColor(.secondary)
}
}
}
}
添加完代码后,在右边画布区域重新resume一下就可以看到画面了
接下来我们接续添加剩余的数据
let sceneries = [
Scenery(id: 1001, name: "金字塔", thumbnailName: "埃及-金字塔-预览", imageName: "埃及-金字塔-黄昏", location: "埃及"),
Scenery(id: 1002, name: "马丘比丘", thumbnailName: "秘鲁-马丘比丘-预览", imageName: "秘鲁-马丘比丘-白天", location: "秘鲁"),
Scenery(id: 1003, name: "乌尤尼盐湖", thumbnailName: "玻利维亚-乌尤尼盐湖-预览", imageName: "玻利维亚-乌尤尼盐湖-近景", location: "玻利维亚"),
Scenery(id: 1004, name: "南极洲", thumbnailName: "南极洲-预览", imageName: "南极洲-陆地", location: "南极洲"),
Scenery(id: 1005, name: "复活岛", thumbnailName: "太平洋-复活岛-预览", imageName: "太平洋-复活岛-白天", location: "太平洋"),
Scenery(id: 1006, name: "加拉帕格斯群岛", thumbnailName: "太平洋-加拉帕格斯群岛-预览", imageName: "太平洋-加拉帕格斯群岛-海上", location: "太平洋"),
Scenery(id: 1007, name: "塞伦盖蒂", thumbnailName: "坦桑尼亚-塞伦盖蒂-预览", imageName: "坦桑尼亚-塞伦盖蒂-草原", location: "坦桑尼亚"),
Scenery(id: 1008, name: "威尼斯", thumbnailName: "意大利-威尼斯-预览", imageName: "意大利-威尼斯-白天", location: "意大利"),
Scenery(id: 1009, name: "泰姬陵", thumbnailName: "印度-泰姬陵-预览", imageName: "印度-泰姬陵-白天", location: "印度"),
Scenery(id: 1010, name: "巨石阵", thumbnailName: "英国-巨石阵-预览", imageName: "英国-巨石阵-白天", location: "英国")
]
第六节 添加导航栏和跳转链接
SwiftUI的导航栏和UIKit中的导航栏有点不一样,SwiftUI的NavigationView是针对某一个视图控件来说的,而在UIKit中我们通常是对控制器进行添加导航栏。
有了之前List View的经验,我们尝试添加导航栏,在List View外面添加一个层NavigationView
然后我们发现,在右边画布的顶部就出现了一篇空白区域
接下来为导航栏添加标题,我们先找到文档看下官方是如何写的
/// Sets the title in the navigation bar for this view.
///
/// Use `navigationBarTitle(_:)` to set the title of the navigation bar.
/// This modifier only takes effect when this view is inside of and visible
/// within a ``NavigationView``.
///
/// The example below shows setting the title of the navigation bar using a
/// ``Text`` view:
struct FlavorView: View {
let items = ["Chocolate", "Vanilla", "Strawberry", "Mint Chip",
"Pistachio"]
var body: some View {
NavigationView {
List(items, id: \.self) {
Text($0)
}
.navigationBarTitle(Text("Today's Flavors"))
}
}
}
有了官方的教学,我们直接照葫芦画瓢
struct ContentView: View {
var body: some View {
NavigationView {
List(sceneries) { item in
Image(item.thumbnailName)
VStack(alignment: .leading) {
Text(item.name)
Text(item.location)
.font(.subheadline)
.foregroundColor(.secondary)
}
}
.navigationBarTitle(Text("风景名胜"))
}
}
}
完成导航栏后,接下来需要让List View的每一行都可以点击跳转到下一个页面
在SwiftUI中使用NavigationLink来触发跳转事件,与OC相比,更简洁了。不需要做事件绑定,只需要在对应的区域写下NavigationLink就可以。
我们使用简单的 “NavigationLink.destination: label:” 这个方法,destination就是跳转的View,label是链接的描述,可以根据需要显示不同描述。
struct ContentView: View {
var body: some View {
NavigationView {
List(sceneries) { item in
Image(item.thumbnailName)
VStack(alignment: .leading) {
Text(item.name)
Text(item.location)
.font(.subheadline)
.foregroundColor(.secondary)
}
NavigationLink(
destination: Text("Destination"),
label: {
Text("Navigate")
})
}
.navigationBarTitle(Text("风景名胜"))
}
}
}
我们把destination中的Text替换为item.name,然后把label中的Text的内容去掉,点击右边画布顶部第一个的“播放”按钮,就可以实现运行的效果了。
在页面刷新之后,点击List View的每一行,都可以看到跳转。
到这里我们就已经完成了第一个页面的全部内容了,是不是非常简单!
我们可以看到代码区域只用了不到30行,就实现了一个List View和跳转功能。
如果换到OC中的话,光是繁杂的delegate就不止这点代码了,更不用说要创建和布局UI控件。