SwiftUI-如何避免将导航硬编码到视图中? [英] SwiftUI - how to avoid navigation hardcoded into the view?

查看:92
本文介绍了SwiftUI-如何避免将导航硬编码到视图中?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我尝试为更大的,可生产的SwiftUI App进行架构设计.我一直在遇到一个相同的问题,这表明SwiftUI的一个主要设计缺陷.

I try to do the architecture for a bigger, production ready SwiftUI App. I am running all the time into the same problem which points to a major design flaw in SwiftUI.

仍然没有人能给我完整的工作,生产就绪的答案.

Still nobody could give me a full working, production ready answer.

如何在SwiftUI中包含导航的可重用视图?

How to do reusable Views in SwiftUI which contain navigation?

由于SwiftUI NavigationLink与视图紧密绑定,因此以较大的应用程序也可以缩放的方式是完全不可能的.是的,在那些小示例应用程序中,NavigationLink可以工作,但是要在一个应用程序中重用许多视图时就不能使用.也许还可以在模块边界上重用. (例如:在iOS,WatchOS等中重用View)

As the SwiftUI NavigationLink is strongly bound to the view this is simply not possible in such a way that it scales also in bigger Apps. NavigationLink in those small sample Apps works, yes - but not as soon as you want to reuse many Views in one App. And maybe also reuse over module boundaries. (like: reusing View in iOS, WatchOS, etc...)

设计问题:NavigationLinks硬编码到视图中.

The design problem: NavigationLinks are hardcoded into the View.

NavigationLink(destination: MyCustomView(item: item))

但是,如果包含此NavigationLink的视图应该可重用,则我无法对目标进行硬编码.必须有一种提供目的地的机制.我在这里问这个问题,得到了很好的答案,但仍然没有完整的答案:

But if the view containing this NavigationLink should be reusable I can not hardcode the destination. There has to be a mechanism which provides the destination. I asked this here and got quite a good answer, but still not the full answer:

SwiftUI MVVM协调器/路由器/NavigationLink

这个想法是将目标链接注入到可重用的视图中.通常,此想法可行,但不幸的是,这并不能扩展到实际的Production Apps.一旦有了多个可重复使用的屏幕,我就会遇到一个逻辑问题,即一个可重复使用的视图(ViewA)需要一个预先配置的视图目标(ViewB).但是,如果ViewB还需要预配置的视图目标ViewC怎么办?我需要以将ViewB注入到ViewA中之前已经将ViewC注入到ViewB中的方式来创建ViewB.依此类推....但是由于当时必须传递的数据不可用,整个构造失败了.

The idea was to inject the Destination Links into the reusable view. Generally the idea works but unfortunately this does not scale to real Production Apps. As soon as I have multiple reusable screens I run into the logical problem that one reusable view (ViewA) needs a preconfigured view-destination (ViewB). But what if ViewB also needs a preconfigured view-destination ViewC? I would need to create ViewB already in such a way that ViewC is injected already in ViewB before I inject ViewB into ViewA. And so on.... but as the data which at that time has to be passed is not available the whole construct fails.

我的另一个想法是使用Environment作为依赖项注入机制来注入NavigationLink的目标.但是我认为,这应该或多或少地被视为一种hack,而不是针对大型应用程序的可扩展解决方案.我们最终将基本上将环境用于所有内容.但是由于Environment也只能在View的内部(而不是在单独的Coordinators或ViewModels中)使用(仅在中使用),因此在我看来,这将再次创建奇怪的构造.

Another idea I had was to use the Environment as dependency injection mechanism to inject destinations for NavigationLink. But I think this should be considered more or less as a hack and not a scalable solution for large Apps. We would end up using the Environment basically for everything. But because Environment also can be used only inside View's (not in separate Coordinators or ViewModels) this would again create strange constructs in my opinion.

像业务逻辑(例如,视图模型代码)和视图必须分开,导航和视图也必须分开(例如协调器模式)在UIKit中,这是可能的,因为我们可以访问后面的UIViewControllerUINavigationController风景. UIKit's MVC已经存在问题,它混和了许多概念,因此变成了有趣的名称"Massive-View-Controller"而不是"Model-View-Controller".现在,类似的问题在SwiftUI中仍然存在,但在我看来甚至更糟.导航和视图是紧密耦合的,不能分离.因此,如果它们包含导航,则不可能进行可重用的视图.可以在UIKit中解决此问题,但是现在在SwiftUI中看不到理智的解决方案.不幸的是,Apple没有向我们提供如何解决此类架构问题的解释.我们只有一些小样本应用程序.

Like business logic (e.g. view model code) and view have to be separated also navigation and view have to be separated (e.g. the Coordinator pattern) In UIKit it's possible because we access to UIViewController and UINavigationController behind the view. UIKit's MVC already had the problem that it mashed up so many concepts that it become the fun-name "Massive-View-Controller" instead of "Model-View-Controller". Now a similar problem continues in SwiftUI but even worse in my opinion. Navigation and Views are strongly coupled and can not be decoupled. Therefore it's not possible to do reusable views if they contain navigation. It was possible to solve this in UIKit but now I can't see a sane solution in SwiftUI. Unfortunately Apple did not provide us an explanation how to solve architectural issues like that. We got just some small sample Apps.

我很想被证明是错误的.请给我展示一个干净的App设计模式,该模式可以解决大量生产就绪的Apps的问题.

I would love to be proven wrong. Please show me a clean App design pattern which solves this for big production ready Apps.

谢谢.

更新:悬赏活动将在几分钟后结束,很遗憾,仍然没有人能够提供有效的示例.但是,如果找不到其他解决方案并将其链接到此处,我将开始一项新的悬赏计划以解决此问题.感谢所有人的巨大贡献!

Update: this bounty will end in a few minutes and unfortunately still nobody was able to provide a working example. But I will start a new bounty to solve this problem if I can't find any other solution and link it here. Thanks to all for their great Contribution!

2020年6月18日更新: 我从苹果公司那里得到了关于这个问题的答案,提出了类似的建议以使视图和模型脱钩:

Update 18th June 2020: I got an answer from Apple regarding this issue, proposing something like this to decouple views and models:

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),它也可以导航.如我所说,这在 小型示例应用程序,但无法扩展到可用于大规模生产的应用程序.

Thanks for the feedback. But as you see you still have the strong coupling in the View. Now "ContentView" needs to know all the views (ViewA, ViewB, ViewC) it can navigate too. As I said, this works in small sample Apps, but it does not scale to big production ready Apps.

想象一下,我在GitHub的一个项目中创建了一个自定义视图.接着 将此视图导入我的应用程序.此自定义视图什么都不知道 关于它也可以导航的其他视图,因为它们是特定的 到我的应用程序.

Imagine that I create a custom View in a Project in GitHub. And then import this view in my App. This custom View does not know anything about the other views it can navigate too, because they are specific to my App.

我希望我能更好地解释这个问题.

I hope I explained the problem better.

我看到的唯一解决此问题的方法是分离 导航和视图类似于UIKit. (例如,UINavigationController)

The only clean solution I see to this problem is to separate Navigation and Views like in UIKit. (e.g. UINavigationController)

谢谢,达克

所以仍然没有干净&这个问题的有效解决方案.期待WWDC2020.

So still no clean & working solution for this problem. Looking forward to WWDC 2020.

推荐答案

闭包就是您所需要的!

struct ItemsView<Destination: View>: View {
    let items: [Item]
    let buildDestination: (Item) -> Destination

    var body: some View {
        NavigationView {
            List(items) { item in
                NavigationLink(destination: self.buildDestination(item)) {
                    Text(item.id.uuidString)
                }
            }
        }
    }
}

我写了一篇关于用闭包替换SwiftUI中的委托模式的文章. https://swiftwithmajid.com/2019/11/06/Swiftui中的关闭功能/

I wrote a post about replacing the delegate pattern in SwiftUI with closures. https://swiftwithmajid.com/2019/11/06/the-power-of-closures-in-swiftui/

这篇关于SwiftUI-如何避免将导航硬编码到视图中?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆