在没有样板的协议中需要一个 SwitftUI 视图
Posted
技术标签:
【中文标题】在没有样板的协议中需要一个 SwitftUI 视图【英文标题】:Require a SwitftUI View in a protocol without boilerplate 【发布时间】:2019-09-20 20:23:06 【问题描述】:[ Ed:一旦我解决了这个问题,我编辑了这个问题的标题,以更好地反映我真正需要的内容。 - 直到我回答了我自己的问题,我才澄清了我需要什么:-)]
我正在 ios 上使用 SwiftUI 开发一个应用程序,在 6 种情况下,我将拥有一个可以选择的项目列表,并且在所有情况下,操作都是移动到显示该项目的屏幕。
我是一个热心的“DRY”倡导者,所以我不想写 6 次列表代码,而是想抽象出列表并选择代码,对于 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】:关键是,如果你想在协议中使用视图,那么:
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 且无样板!
【讨论】:
以上是关于在没有样板的协议中需要一个 SwitftUI 视图的主要内容,如果未能解决你的问题,请参考以下文章