让不同类型支持ForEach
什么是ForEach
SwiftUI的ForEach类型使我们能够通过转换集合中的每个元素来创建一系列视图。但是,由于ForEach重用了它创建的视图以优化性能(就像其他基于列表的视图一样,如UITableView和UICollectionView一样),它要求我们提供一种方法来标识我们所基于的每个元素它对
当我们要转换的元素符合Identifiable协议时,这种标识就会自动处理,我们可以将值的集合直接传递给ForEach。例如,在这里,我们将一组用户值转换为垂直排列的UserView实例的列表:
struct User: Identifiable {
let id: UUID
var name: String
}
struct UserList: View {
var users = [User]()
var body: some View {
VStack {
ForEach(users) { user in
UserView(user: user)
}
}
}
}
但是,有时我们可能希望将ForEach基于更简单的原始值(例如字符串)的集合。这样做一开始似乎很困难,因为我们不想让String无条件地符合Identifiable。幸运的是,有一种方法可以做到这一点,方法是使用 .self键路径来计算每个元素的标识符-就像这样
struct TagList: View {
var tags: [String]
var body: some View {
HStack {
// Using '.self', we can refer to each element directly,
// and use the element's own value as its identifier:
ForEach(tags, id: .self) { tag in
Text(tag)
.padding(3)
.background(Color.secondary)
.cornerRadius(5)
}
}
}
}
当然,另一种选择是使用可识别类型包装原始值,例如这样
truct Tag: Identifiable {
var id: String { name }
var name: String
}
struct TagList: View {
var tags: [Tag]
var body: some View {
HStack {
ForEach(tags) { tag in
Text(tag.name)
.padding(3)
.background(Color.secondary)
.cornerRadius(5)
}
}
}
}
使用以上两种技术中的任何一种时,首先要确保我们处理的值都是唯一的(至少在该集合中),这很重要,因为否则ForEach可能会错误地重用结果视图。
如果我们计划在整个代码库中的多个位置使用原始值的ForEach,则可能需要创建一个简单的便捷API来执行此操作,以避免在所有这些位置重复使用 .self键路径参数:
extension ForEach where Data.Element: Hashable, ID == Data.Element, Content: View {
init(values: Data, content: @escaping (Data.Element) -> Content) {
self.init(values, id: .self, content: content)
}
}
通过上述扩展,我们现在可以轻松地将任何原始值的集合(例如字符串和整数)传递给ForEach,如下所示:
struct TagList: View {
var tags: [String]
var body: some View {
HStack {
ForEach(values: tags) { tag in
Text(tag)
.padding(3)
.background(Color.secondary)
.cornerRadius(5)
}
}
}
}
- SwiftUI - init
SwiftUI 中init方法,会在编译期进行预加载
init() {
}
- List样式设置init方法中预onAppear中的区别
// 全局生效,以项目中最后加载的init方法中的设置为准
init() {
UITableView.appearance().sectionFooterHeight = 10
UITableView.appearance().backgroundColor = UIColor.red
UITableViewCell.appearance().backgroundColor = UIColor.red
}
// 当前页面生效
.onAppear() {
UITableView.appearance().sectionFooterHeight = 10
UITableView.appearance().backgroundColor = UIColor.red
UITableViewCell.appearance().backgroundColor = UIColor.red
}
- List自定义组尾视图
struct Footer: View {
var body: some View {
Rectangle()
.foregroundColor(.white)
.listRowInsets(EdgeInsets())
}
}
struct Timeline : View {
var body: some View {
List {
Section(footer: Footer()) {
Text("Item 1")
Text("Item 2")
Text("Item 3")
}
}
}
}
List {
Section(footer: Text(""))) {
Text("One")
Text("Two")
Text("Three")
}
}
List {
Section(footer: Text("")) {
Text("My text")
}
EmptyView()
}
List {
Text("Item 1")
Text("Item 2")
// Adding empty section with footer
Section(footer:
Rectangle()
.foregroundColor(.clear)
.background(Color(.systemBackground))){EmptyView()}
.padding(.horizontal, -15)
}