将操作作为()->无效传递给组件是一个好主意吗?

时间:2020-04-02 15:27:22

标签: swift swiftui

我是swfitUI的新手,并且正在构建一个像这样的组件:

// my solution
struct TodoItem {

  var title: String
  var action: (() -> Void)?

  var body: some View {
    HStack {
      Text(title)
      if let action = action {
        Button(action: action, label: Image(image))
      }
    }
  }
}

但是我的队友不同意,他们认为我不应该将动作传递给组件,而是应该像这样使用ViewBuilder

// my teammates' solution
struct TodoItem<Content>: View where Content: View {
  var title: String
  var content: Content

  @inlinable public init(title: String, @ViewBuilder content: () -> Content) {
    self.title = title
    self.content = content()
  }

  var body: some View {
    HStack {
      Text(title)
      content
    }
  }
}

他们说这是更加SwiftUI的方式,但是我不了解,在用法上,在我的解决方案中,任何使用此组件的人都只需要关心标题和动作,就在队友中,用户需要要关心标题,操作以及如何构建按钮,很明显,我的解决方案更易于使用,而且我的解决方案没有任何缺点。

我的队友的解决方案比我的更好吗?

2 个答案:

答案 0 :(得分:3)

如果您发现自己想要使用AnyView,那么您就离开了SwiftUI的幸福道路。有一个原因将其列在文档的Infrequently Used Views部分中。

AnyView破坏了许多SwiftUI的优化。当您别无选择时,它作为逃生舱口存在。

您的代码看起来像我到目前为止从Apple看到的所有示例。如果您的目标是调用者生成的View的容器,并且您希望实现细节由调用者决定,那么@ViewBuilder就很有意义。 HStack是一个很好的例子。如果组件应该封装View实现的详细信息,那么它应该使用传递的属性(您正在执行的操作)生成视图本身。因此,在这种情况下的问题是“ TodoItem是否是将由许多不同的调用者以许多不同方式使用的通用工具?”如果没有,我不确定为什么要通过ViewBuilder。


您的更新(删除了AnyView)极大地改变了问题。在这种情况下,可以归结为我在上面的最后一段:如果TodoItem旨在用作调用者应为其提供内容的通用容器,则ViewBuilder很好。假设TodoItem是关于布局而不是关于显示,例如HStack。

但是,如果是关于显示的视图,例如Button或Text,则应传递它的属性并让其管理其内部。 (请注意,按钮允许传递标签ViewBuilder,但通常不需要这样做。)

像“ TodoItem”这样的名称听起来很像后者;似乎是一个自定义视图,应该管理自己的外观。但是主要的问题是:有多少调用者将不同的View传递给此ViewBuilder?如果所有调用者都传递几乎相同的视图(或者只有一个调用者),则它应该是属性(如Button)。如果有许多调用方传递不同类型的内容视图,则应使用ViewBuilder(如HStack)。

“更多SwiftUI”也不是。他们解决了SwiftUI中的各种问题。

答案 1 :(得分:1)

您的方法是正确的,只有更改(let不起作用),因此请更正以下内容:

struct TodoItem {

  var title: String
  var image: String          // << this might also needed
  var action: (() -> Void)?

  var body: some View {
    HStack {
      Text(title)
      if action != nil {      // << here !!
        Button(action: action!, label: { Image(image) })
      }
    }
  }
}

通过Xcode 11.4 / iOS 13.4测试

关于队友的备用:在成员中存储View不是“ SwiftUI方式” ...因此,修复第二个变体后,我会将ViewBuilder存储到成员中并使用它将View注入body内。但是无论如何,这是更糟糕的方法,因为这会破坏UI组件的完整性。