[Ed:一旦解决了这个问题,我就编辑了这个问题的标题,以更好地反映我的实际需求。 -直到我回答了自己的问题,我才澄清了我的需要:-)]
我正在IOS上使用SwiftUI开发一个应用程序,在这种情况下,我有6种情况,其中会有一个可供选择的项目列表,在所有情况下,操作都将移至显示该项目的屏幕。
我是“ DRY”的敏锐拥护者,所以我不想写6次列表代码,而是要抽象出列表并选择代码,对于6种情况的每一种,我只想提供该实例的独特之处。 / p>
我想使用协议,但希望将样板降至最低。
我的协议和相关支持如下:
import SwiftUI
/// -----------------------------------------------------------------
/// ListAndSelect
/// -----------------------------------------------------------------
protocol ListAndSelectItem: Identifiable {
var name: String { get set }
var value: Int { get set }
// For listView:
static var listTitle: String { get }
associatedtype ItemListView: View
func itemListView() -> ItemListView
// For detailView:
var detailTitle: String { get }
associatedtype DetailView: View
func detailView() -> DetailView
}
extension Array where Element: ListAndSelectItem {
func listAndSelect() -> some View {
return ListView(items: self, itemName: Element.listTitle)
}
}
struct ListView<Item: ListAndSelectItem>: View {
var items: [Item]
var itemName: String
var body: some View {
NavigationView {
List(items) { item in
NavigationLink(
destination: DetailView(item: item, index: String(item.value))
) {
VStack(alignment: .leading){
item.itemListView()
.font(.system(size: 15)) // Feasible that we should remove this
}
}
}
.navigationBarTitle(Text(itemName).foregroundColor(Color.black))
}
}
}
struct DetailView<Item: ListAndSelectItem>: View {
@Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
var item: Item
var index: String
var body: some View {
NavigationView(){
item.detailView()
}
.navigationBarTitle(Text(item.name).foregroundColor(Color.black))
.navigationBarItems(leading: Button(action: {
self.presentationMode.wrappedValue.dismiss()
}, label: { Text("<").foregroundColor(Color.black)}))
}
}
这意味着我可以这样写:
struct Person: ListAndSelectItem {
var id = UUID()
var name: String
var value: Int
typealias ItemListView = PersonListView
static var listTitle = "People"
func itemListView() -> PersonListView {
PersonListView(person: self)
}
typealias DetailView = PersonDetailView
let detailTitle = "Detail Title"
func detailView() -> DetailView {
PersonDetailView(person: self)
}
}
struct PersonListView: View {
var person: Person
var body: some View {
Text("List View for \(person.name)")
}
}
struct PersonDetailView: View {
var person: Person
var body: some View {
Text("Detail View for \(person.name)")
}
}
struct ContentView: View {
let persons: [Person] = [
Person(name: "Jane", value: 1),
Person(name: "John", value: 2),
Person(name: "Jemima", value: 3),
]
var body: some View {
persons.listAndSelect()
}
}
这还不错,但我认为我应该可以走得更远。
必须写:
typealias ItemListView = PersonListView
static var listTitle = "People"
func itemListView() -> PersonListView {
PersonListView(person: self)
}
使用
struct PersonListView: View {
var person: Person
var body: some View {
Text("List View for \(person.name)")
}
}
对我来说仍然很麻烦。 在我的6种情况下,我都会编写非常相似的代码。 我觉得我应该能够写:
static var listTitle = "People"
func itemListView() = {
Text("List View for \(name)")
}
}
因为这是唯一的位。 但这当然不会编译。
然后在“细节”中相同。
我不知道如何进一步简化。 有什么想法欢迎吗?
答案 0 :(得分:0)
关键是,如果要在协议中使用视图,则:
1)在协议中:
associatedtype SpecialView: View
var specialView: SpecialView { get }
2)在使用协议的结构中:
var specialView: some View { Text("Special View") }
因此,在出现问题的情况下:
通过将我的协议更改为:
protocol ListAndSelectItem: Identifiable {
var name: String { get set }
var value: Int { get set }
// For listView:
static var listTitle: String { get }
associatedtype ListView: View
var listView: ListView { get }
// For detailView:
var detailTitle: String { get }
associatedtype DetailView: View
var detailView: DetailView { get }
}
我现在可以将Person定义为:
struct Person: ListAndSelectItem {
var id = UUID()
var name: String
var value: Int
static var listTitle = "People"
var listView: some View { Text("List View for \(name)") }
var detailTitle = "Person"
var detailView: some View { Text("Detail View for \(name)") }
}
它适合干燥并且没有样板!