使用 SwiftUI 在不同的 UI 层次结构之间切换的正确方法是啥?

Posted

技术标签:

【中文标题】使用 SwiftUI 在不同的 UI 层次结构之间切换的正确方法是啥?【英文标题】:What is the correct way to switch between distinct UI hierarchies with SwiftUI?使用 SwiftUI 在不同的 UI 层次结构之间切换的正确方法是什么? 【发布时间】:2019-09-04 00:13:20 【问题描述】:

想象一个典型的应用程序,它具有引导、登录/注册和某种内容。当应用程序加载时,您需要决定要显示哪个视图。一个简单的实现可能如下所示:

struct ContentView: View 
    //assuming some centralized state that keeps track of basic user activity
    @State var applicationState = getApplicationState()

    var body: some View 

        if !applicationState.hasSeenOnboarding 
            return OnBoarding()
        

        if !applicationState.isSignedIn 
            return Registration()
        

        return MainContent()
    

显然这种方法失败了,因为 SwiftUI 视图需要不透明的返回类型 some View。这可以使用AnyView 包装器类型来缓解(尽管有点骇人听闻),它提供类型擦除并允许编译下面的代码。

struct ContentView: View 
    //assuming some centralized state that keeps track of basic user activity
    @State var applicationState = getApplicationState()

    var body: some View 

        if !applicationState.hasSeenOnboarding 
            return AnyView(OnBoarding())
        

        if !applicationState.isSignedIn 
            return AnyView(Registration())
        

        return AnyView(MainContent())
    

有没有更正确的方法不需要使用AnyViewSceneDelegate 中是否有可以处理转换到完全不同的视图层次结构的功能?

【问题讨论】:

您可以使用 ZStack 代替 AnyView。 这在技术上确实有效。有没有更好的方法来处理这种情况? else if 我不是两个单独的if 语句?诚实地?很难说 - 但很好的问题和赞成。事情是这样的——Swift 开发最佳实践花了多长时间? (有吗?)人们可能会称这是一个糟糕的问题,因为它征求意见。让我给你两个。 (1)UIKit是如何处理的?在RxSwift?我这里没有好的答案。 (2) 也许将尽可能多的逻辑移入模型或ObservedObject,实际上返回 any View?我想这取决于对于仍然相当脆弱的测试平台的“有没有更好的方法”的意思。 感谢@dfd,我的直觉是,这可能是 SceneDelegate 可以处理的情况,但我在 src 中没有发现任何实际支持这种直觉的东西。猜猜我只是想弄清楚其他早期的 SwiftUI 探索者是否遇到过这个问题并更优雅地解决了这个问题。今晚晚些时候我将尝试使用 Group 方法! @superpuccio 是很好的回答者之一。我使用这种技术Group 来保持图像选择器全屏显示或显示我的“正常” UI - 它运行良好。 【参考方案1】:

可能最SwiftUI-y 执行此类操作的方法是使用Group 视图:

import SwiftUI

struct ContentView: View 
    @State private var applicationState = getApplicationState()

    var body: some View 
        Group 
            if !applicationState.hasSeenOnboarding 
                OnBoarding()
             else if !applicationState.isSignedIn 
                Registration()
             else 
                MainContent()
            
        
    


struct ContentView_Previews: PreviewProvider 
    static var previews: some View 
        ContentView()
    

需要注意的最重要的事情是,这样一来,您就不会依赖 AnyView 的类型擦除(如果不是绝对必要的话,可以避免)。

如果您想将初始视图创建封装在一个方法中,请不要使用类型擦除。相反,请使用 some 关键字:

import SwiftUI

struct ContentView: View 
    @State private var applicationState = getApplicationState()

    private func initialView() -> some View 
        if !applicationState.hasSeenOnboarding 
            OnBoarding()
         else if !applicationState.isSignedIn 
            Registration()
         else 
            MainContent()
        
    

    var body: some View 
        initialView()
    


struct ContentView_Previews: PreviewProvider 
    static var previews: some View 
        ContentView()
    

【讨论】:

谢谢!这似乎是目前要走的路。

以上是关于使用 SwiftUI 在不同的 UI 层次结构之间切换的正确方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

SwiftUI:在视图层次结构中自动来回导航

SwiftUI 视图层次结构中较高的动画覆盖嵌套动画

Android Studio 4.0+ 中新的 UI 层次结构调试工具

如何在没有导航回溯的情况下启动 SwiftUI 视图?

SwiftUI 在一种情况下而不是另一种情况下查看层次结构警告

如何将不在根层次结构中的 SwiftUI 视图呈现为 UIImage?