本文为 Eul 样章,如果您喜欢,请移步 AppStore/Eul 查看更多内容。
Eul 是一款 SwiftUI & Combine 教程 App(iOS、macOS),以文章(文字、图片、代码)配合真机示例(Xcode 12+、iOS 14+,macOS 11+)的形式呈现给读者。笔者意在尽可能使用简洁明了的语言阐述 SwiftUI & Combine 相关的知识,使读者能快速掌握并在 iOS 开发中实践。
List 最基本的构建方法:
List { Text("Sun") Text("Cloud") Text("Snow") }
通常我们的列表并非静态,而是与动态的数据源绑定的。List 可以通过传入 data
来实现,不过这里的 data 需要遵循 Identifiable 协议,这样才能保证数据的唯一性。
我们先对天气作如下定义:
struct Weather: Identifiable { let id = UUID() let name: String let icon: String }
id
是协议要求实现的属性(遵循 Hashable 协议),我们也可以在实例初始化时传入 0, 1, 2, 3...只要是没有冲突的哈希值都可以。这里我们有个简单的处理方法,就是通过 UUID()
生成惟一值,在初始化时可以省去传参。
然后我们添加数据源:
@State private var weathers = [ Weather(name: "Sunshine", icon: "sun.max.fill"), Weather(name: "Cloud", icon: "cloud"), Weather(name: "Snow", icon: "snow"), Weather(name: "Rain", icon: "cloud.rain.fill") ]
生成 List:
List(weathers) { v in Label(v.name, systemImage: v.icon) }
List 可以通过树形结构的数据源直接构建可展开的列表,比如我们定义如下可展开的天气对象:
struct ExpandWeather: Identifiable { let id = UUID() var name: String var icon: String var weathers: [ExpandWeather]? }
该结构体内嵌的 weathers 的元素类型就是它本身,而且是可选类型。我们构造如下数据:
let expandWeather: [ExpandWeather] = [ ExpandWeather(name: "Weather", icon: "", weathers: [ ExpandWeather(name: "Sunshine", icon: "sun.max.fill"), ExpandWeather(name: "Cloud", icon: "cloud"), ExpandWeather(name: "Snow", icon: "snow"), ExpandWeather(name: "Rain", icon: "cloud.rain.fill") ]) ]
然后通过如下方法构建视图:
List(expandWeather, children: \.weathers) { weather in Label(weather.name, systemImage: weather.icon) }
最后得到的就是一个可以展开的列表,当然我们可以构造更加复杂的多级展开列表,其原理是一样的,都是在子数据中嵌套更多数据类型相同的子数据。
List 有多种样式,系统为我们提供了多种 ListStyle。其中,iOS 比较常用的除了系统默认样式外,还有 GroupedListStyle 和 InsetGroupedListStyle。
ForEach 能通过循环从集合数据中快速构建视图。集合数据 data 可以是 Range<Int>
类型,也可以是元素遵循 Identifiable
协议的数组。
比如:
ForEach(0..<2) { idx in Text("\(idx)") }
或者,我们仍然以上文的 weathers 作为 data ,
ForEach(weathers) { weather in Text(weather.name) }
ForEach 里有个参数 id,它用来保证数据的唯一性。上例中我们并没有显示地调用它,因为对 Range
如果我们只是想通过一个普通的字符串数组去实现循环创建视图,那么 id 可以显示指定为 \.self
,即使用字符串本身作为 id 来保证数据的唯一性。
ForEach(["A", "B"], id: \.self) { str in Text(str) }
还记得我们前面提到过得 EditButton 吗?之前我们并没有具体讲解它的用法,现在我们可以见到它的具体使用了。
ForEach 和 List 配合使用,可以轻松地对列表进行编辑操作:delete、move。还是以 weathers 作为数据源,我们构建列表视图:
var body: some View { List { ForEach(weathers) { v in Label(v.name, systemImage: v.icon) } .onDelete(perform: onDelete) .onMove(perform: onMove) } .navigationBarItems(trailing: EditButton()) } func onDelete(offsets: IndexSet) { weathers.remove(atOffsets: offsets) } func onMove(fromOffsets: IndexSet, toOffset: Int) { weathers.move(fromOffsets: fromOffsets, toOffset: toOffset) }
EditButton 终于登场了,试试对列表进行编辑操作。
这里的编辑操作只是单选操作,如果我们要多选呢?可以在 List 中绑定 selection 数据,通过它对多条数据同时操作。这里就不举例了,读者可以按照这个思路去实现。
ScrollView 只有一个构建方法:
init(_ axes: Axis.Set = .vertical, showsIndicators: Bool = true, content: () -> Content)
使用也简单,这里不再赘述。
ScrollViewReader 的回调闭包返回的是一个 ScrollViewProxy 实例,它只有一个实例方法 scrollTo()
,该 方法能够使 ScrollView 滑动到指定的位置,对 List 也同样适用。
scrollTo()
还可以传入 anchor
参数控制更精确的滑动,比如示例所示为:sr.scrollTo(70, anchor: .center)
。
如果需要给滑动添加动画效果,只需要在 withAnimation 的闭包中调用 scrollTo()
即可。
本文为 Eul 样章,如果您喜欢,请移步 AppStore/Eul 查看更多内容。