[英]How to transition Views programmatically using SwiftUI?
I want to show the user another view when the login is successful, otherwise stay on that view.我想在登录成功时向用户显示另一个视图,否则留在该视图上。 I've done that with UIKit by performing a segue.我已经通过执行转场使用 UIKit 做到了这一点。 Is there such an alternative in SwiftUI? SwiftUI 中有这样的选择吗?
The NavigationButton solution does not work as I need to validate the user input before transitioning to the other view. 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")
}
Xcode 11 beta 5. Xcode 11 测试版 5。
NavigationDestinationLink
and NavigationButton
have been deprecated and replaced by NavigationLink
. NavigationDestinationLink
和NavigationButton
已被弃用并由NavigationLink
取代。
Here's a full working example of programatically pushing a view to a NavigationView.这是以编程方式将视图推送到 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
Note that the NavigationLink
entity has to be present inside the View body.请注意, NavigationLink
实体必须存在于视图主体内。 If you have a button that triggers the link, you'll use the label of the NavigationLink.如果您有触发链接的按钮,您将使用 NavigationLink 的标签。 In this case, the NavigationLink is hidden by setting its frame to 0,0, which is kind of a hack but I'm not aware of a better method at this point.在这种情况下,NavigationLink 通过将其框架设置为 0,0 来隐藏,这是一种黑客行为,但我目前不知道更好的方法。 .hidden() doesn't have the same effect. .hidden() 没有同样的效果。
You could do it like bellow, based on this response (it's packed like a Playground for easy testing:根据此响应,您可以像下面那样做(它像 Playground 一样打包以便于测试:
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())
Although this will work, I think that, from an architectural perspective, you try to push an "imperative programming" paradigm into SwiftUI's reactive logic.虽然这会奏效,但我认为,从架构的角度来看,您尝试将“命令式编程”范式推入 SwiftUI 的响应式逻辑中。 I mean, I would rather implement it with the login logic wrapped into an ObjectBinding
class with an exposed isLoggedin
property and make the UI react to the current state (represented by isLoggedin
).我的意思是,我宁愿将登录逻辑封装到ObjectBinding
类中,并带有公开的isLoggedin
属性来实现它,并使 UI 对当前状态(由isLoggedin
表示)做出反应。
Here's a very high level example :这是一个非常高级的示例:
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")
}
}
}
}
I used a Bool state for my login transition, it seems pretty fluid.我使用 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)
}
}
}
}
This can also be done programmatically such as below: 也可以通过编程方式完成,例如:
let vc = self.storyboard?.instantiateViewController(withIdentifier: "TheTargetViewController")
self.present(vc!, animated: true, completion: nil)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.