SwiftUI 九

让不同类型支持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)
}
 

posted on 2020-04-11 15:34  码上翻身  阅读(307)  评论(0编辑  收藏  举报

导航