在没有样板的协议中需要一个 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 视图的主要内容,如果未能解决你的问题,请参考以下文章

在 SwiftUI 中设置导航栏项目样式

ifc用revit怎么打开

swift 视图模型,样板

Swift:我的委托协议方法需要在另一个视图控制器上执行

在 redux 中使用样板动作和 reducer

添加委托协议后,应用程序在 main 中崩溃;没有错误代码