我尝试为一个更大的、生产就绪的 SwiftUI 应用程序设计架构。我总是遇到同样的问题,这个问题指向 SwiftUI 的一个主要设计缺陷。
仍然没有人能给我一个完整的工作,生产现成的答案。
如何在包含导航的 SwiftUI
中实现可重用视图?
由于 SwiftUI
NavigationLink
是强烈的视图绑定,这是根本不可能的,以这样的方式,它的规模也在较大的应用程序。NavigationLink
在这些小样本应用程序的工作,是的-但不尽快你想重用一个应用程序中的许多视图。也许还可以在模块边界上重用。(例如: 在 iOS、 WatchOS 等系统中重用 View..。.)
设计问题: NavigationLinks 被硬编码到视图中。
NavigationLink(destination: MyCustomView(item: item))
但是如果包含这个 NavigationLink
的视图应该是可重用的 我不会硬编码目标。必须有一个机制来提供目的地。我在这里问了这个问题,得到了一个很好的答案,但仍然没有得到完整的答案:
其想法是将目标链接注入到可重用视图中。一般来说,这个想法是可行的,但不幸的是,这个想法并不适用于真正的生产应用程序。一旦我有了多个可重用屏幕,我就会遇到一个逻辑问题,即一个可重用视图(ViewA
)需要一个预配置的视图-目的地(ViewB
)。但是,如果 ViewB
也需要预先配置的视图目标 ViewC
,那该怎么办呢?我需要创建 ViewB
已经在这样的方式,ViewC
已经注射到 ViewB
之前,我注射到 ViewA
的 ViewB
。等等。但由于当时必须传递的数据不可用,整个构造就会失败。
我的另一个想法是使用 Environment
作为依赖注入机制,为 NavigationLink
注入目的地。但我认为这或多或少应该被视为一种技巧,而不是大型应用程序的可扩展解决方案。我们最终会把环境基本上用于所有的事情。但是因为环境也可以在视图中使用 只有(不在单独的协调器或视图模型中) ,在我看来这又会产生奇怪的结构。
就像业务逻辑(例如视图模型代码)和视图必须分开一样,导航和视图也必须分开(例如协调器模式)在 UIKit
中这是可能的,因为我们访问视图后面的 UIViewController
和 UINavigationController
。UIKit's
MVC 已经有了这样的问题,它混合了这么多的概念,它成为一个有趣的名称“大规模视图控制器”,而不是“模型-视图-控制器”。现在类似的问题继续在 SwiftUI
,但在我看来甚至更糟。导航和视图是强耦合的,不能解耦。因此,如果包含导航,就不可能执行可重用视图。在 UIKit
中解决这个问题是可能的,但是现在我在 SwiftUI
中看不到一个理智的解决方案。不幸的是,苹果并没有向我们解释如何解决这样的架构问题。我们只有一些小样本应用程序。
我很乐意被证明是错的。请告诉我一个干净的应用程序设计模式,解决这个大生产准备应用程序。
先谢谢你。
更新: 这个奖金将在几分钟内结束,不幸的是,仍然没有人能够提供一个工作的例子。但是如果我找不到其他的解决方法,我会开始一个新的悬赏来解决这个问题。感谢所有人的伟大贡献!
2020年6月18日更新: 关于这个问题,我从苹果公司得到了一个答案,提出了类似这样的东西来解耦视图和模型:
enum Destination {
case viewA
case viewB
case viewC
}
struct Thing: Identifiable {
var title: String
var destination: Destination
// … other stuff omitted …
}
struct ContentView {
var things: [Thing]
var body: some View {
List(things) {
NavigationLink($0.title, destination: destination(for: $0))
}
}
@ViewBuilder
func destination(for thing: Thing) -> some View {
switch thing.destination {
case .viewA:
return ViewA(thing)
case .viewB:
return ViewB(thing)
case .viewC:
return ViewC(thing)
}
}
}
我的回答是:
谢谢你的反馈,但正如你所看到的,你仍然有强大的 现在“ ContentView”需要知道所有的视图 (ViewA,ViewB,ViewC)它也可以导航。正如我所说,这在 小样本的应用程序,但它不能扩展到大规模生产就绪的应用程序。
假设我在 GitHub 中的一个项目中创建了一个自定义视图 导入这个视图在我的应用程序。这个自定义视图不知道任何事情 它也可以浏览其他视图,因为它们是特定的 我的应用程序。
我希望我能更好地解释这个问题。
对于这个问题,我认为唯一干净的解决办法是分离 像 UIKit 中的导航和视图(例如 UINavigationController)
谢谢,达科
因此,仍然没有清洁和工作的解决方案,这个问题。期待2020年 WWDC。
2021年9月更新:
对于这个问题,使用 AnyView
不是一个好的通用解决方案。在大型应用程序中,基本上所有视图都必须以可重用的方式进行设计。这将意味着 AnyView
得到使用 无处不在。我与两个苹果开发者进行了一次会议,他们清楚地向我解释了 AnyView
创建的性能比 View 差得多,它应该只在特殊情况下使用。这样做的根本原因是,在编译期间无法解析 AnyView
的类型,因此必须在堆上分配它。
2022年6月更新:
苹果今天在全球开发者大会上推出了新的 SwiftUI NavigationStack
。
Https://developer.apple.com/documentation/swiftui/navigationstack/
通过使用 .navigationDestination
修饰符,NavigationStack
允许将目标视图与当前可见视图分离。这终于成为一个干净的协调员了。
感谢收听@Apple!