在没有样板的协议中需要 SwitftUI 视图
Require a SwitftUI View in a protocol without boilerplate
[ Ed:解决这个问题后,我编辑了这个问题的标题,以更好地反映我的实际需要。 - 直到我回答了我自己的问题,我才弄清楚我需要什么:-)]
我正在 IOS 上使用 SwiftUI 开发一个应用程序,在 6 种情况下,我将拥有一个项目列表,我可以 select 在所有情况下,操作都是移动显示该项目的屏幕。
我是一个热心的 "DRY" 提倡者,所以我不想将列表代码写 6 次,而是想抽象出列表和 select 代码以及我只想提供的 6 个场景中的每一个该实例的独特之处。
我想使用协议,但希望将样板代码保持在最低限度。
我的协议和相关支持是这样的:
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)")
}
}
因为那是独一无二的。
但这肯定不会编译。
然后对于详细信息也是如此。
我不知道如何进一步简化。
欢迎提出任何想法?
关键是,如果你想在协议中使用视图,那么:
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)") }
}
适合 DRY 并且没有样板文件!
[ Ed:解决这个问题后,我编辑了这个问题的标题,以更好地反映我的实际需要。 - 直到我回答了我自己的问题,我才弄清楚我需要什么:-)]
我正在 IOS 上使用 SwiftUI 开发一个应用程序,在 6 种情况下,我将拥有一个项目列表,我可以 select 在所有情况下,操作都是移动显示该项目的屏幕。
我是一个热心的 "DRY" 提倡者,所以我不想将列表代码写 6 次,而是想抽象出列表和 select 代码以及我只想提供的 6 个场景中的每一个该实例的独特之处。
我想使用协议,但希望将样板代码保持在最低限度。
我的协议和相关支持是这样的:
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)")
}
}
因为那是独一无二的。 但这肯定不会编译。
然后对于详细信息也是如此。
我不知道如何进一步简化。 欢迎提出任何想法?
关键是,如果你想在协议中使用视图,那么:
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)") }
}
适合 DRY 并且没有样板文件!