【问题标题】:How to transition Views programmatically using SwiftUI?如何使用 SwiftUI 以编程方式转换视图?
【发布时间】:2019-07-22 16:16:01
【问题描述】:

我想在登录成功时向用户显示另一个视图,否则留在那个视图上。我已经通过执行 segue 来使用 UIKit 做到这一点。 SwiftUI 中有这样的替代方案吗?

NavigationButton 解决方案不起作用,因为我需要在转换到另一个视图之前验证用户输入。

Button(action: {
    let authService = AuthorizationService()
    let result = authService.isAuthorized(username: self.username, password: self.password)
    if(result == true) {
        print("Login successful.")
        // TODO: ADD LOGIC
        *** HERE I WANT TO PERFORM THE SEGUE ***

        presentation(MainView)
    } else {
        print("Login failed.")
    }
}) {
    Text("Login")
}

【问题讨论】:

  • 我已经发布了一个answer here,可以方便地使用NavigationViewNavigationLink
  • 签出 SwiftUI 导航库 github.com/canopas/UIPilot 以便于导航。

标签: swift swiftui


【解决方案1】:

Xcode 11 测试版 5。

NavigationDestinationLinkNavigationButton 已被弃用并被 NavigationLink 取代。

这是一个以编程方式将视图推送到 NavigationView 的完整工作示例。

import SwiftUI
import Combine


enum MyAppPage {
    case Menu
    case SecondPage
}

final class MyAppEnvironmentData: ObservableObject {
    @Published var currentPage : MyAppPage? = .Menu
}

struct NavigationTest: View {

    var body: some View {
        NavigationView {
            PageOne()
        }
    }
}


struct PageOne: View {
    @EnvironmentObject var env : MyAppEnvironmentData


    var body: some View {
        let navlink = NavigationLink(destination: PageTwo(),
                       tag: .SecondPage,
                       selection: $env.currentPage,
                       label: { EmptyView() })

        return VStack {
            Text("Page One").font(.largeTitle).padding()

            navlink
            .frame(width:0, height:0)

            Button("Button") {
                self.env.currentPage = .SecondPage
            }
            .padding()
            .border(Color.primary)

        }
    }

}


struct PageTwo: View {

    @EnvironmentObject var env : MyAppEnvironmentData

    var body: some View {
        VStack {
            Text("Page Two").font(.largeTitle).padding()

            Text("Go Back")
            .padding()
            .border(Color.primary)
            .onTapGesture {
                self.env.currentPage = .Menu
            }
        }.navigationBarBackButtonHidden(true)
    }
}

#if DEBUG
struct NavigationTest_Previews: PreviewProvider {
    static var previews: some View {
        NavigationTest().environmentObject(MyAppEnvironmentData())
    }
}
#endif

请注意,NavigationLink 实体必须存在于视图主体中。 如果您有一个触发链接的按钮,您将使用 NavigationLink 的标签。 在这种情况下,通过将 NavigationLink 的框架设置为 0,0 来隐藏 NavigationLink,这是一种 hack,但我目前不知道有更好的方法。 .hidden() 没有相同的效果。

【讨论】:

  • 抱歉,您是否提到您需要更改 SceneDelegate.swift 上的这一行才能使其正常工作。让 contentView = NavigationTest().environmentObject(MyAppEnvironmentData()) 仅供参考
【解决方案2】:

根据this 响应,您可以像下面这样进行操作(它像游乐场一样打包,以便于测试:

import SwiftUI
import Combine
import PlaygroundSupport


struct ContentView: View {
    var body: some View {
        NavigationView {
            MainView().navigationBarTitle(Text("Main View"))
        }
    }
}

struct MainView: View {
    let afterLoginView = DynamicNavigationDestinationLink(id: \String.self) { message in
        AfterLoginView(msg: message)
    }

    var body: some View {
        Button(action: {
            print("Do the login logic here")
            self.afterLoginView.presentedData?.value = "Login successful"
        }) {
            Text("Login")
        }
    }
}

struct AfterLoginView: View {
    let msg: String

    var body: some View {
        Text(msg)
    }
}

PlaygroundPage.current.liveView = UIHostingController(rootView: ContentView())

虽然这可行,但我认为,从架构的角度来看,您尝试将“命令式编程”范式推入 SwiftUI 的反应式逻辑中。 我的意思是,我宁愿将登录逻辑封装到具有暴露的isLoggedin 属性的ObjectBinding 类中来实现它,并使UI 对当前状态做出反应(由isLoggedin 表示)。

这是一个非常高级的例子:

struct MainView: View {
    @ObjectBinding private var loginManager = LoginManager()

    var body: some View {
        if loginManager.isLoggedin {
            Text("After login content")
        } else {
            Button(action: {  
                 self.loginManager.login()
            }) {
                 Text("Login")
            }
        }
    }
}

【讨论】:

    【解决方案3】:

    我使用 Bool 状态进行登录转换,它看起来很流畅。

    struct ContentView: View {
        @State var loggedIn = false
        
        var body: some View {
            VStack{
                if self.loggedIn {
                    Text("LoggedIn")
                    Button(action: {
                        self.loggedIn = false 
    
                    }) {
                        Text("Log out")
                    }
                } else {
                    LoginPage(loggedIn: $loggedIn)
                }
            }
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-12-10
      相关资源
      最近更新 更多