如何使用SwiftUI在程序中以编程方式过渡视图?

6

当登录成功时,我希望向用户展示另一个视图,否则保持当前视图。在UIKit中,我通过执行segue来实现这一点。在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")
}

我在这里发布了答案,方便地使用了NavigationView和NavigationLink。 - staticVoidMan
请查看 SwiftUI 导航库 github.com/canopas/UIPilot,以便轻松进行导航。 - jimmy0251
3个回答

4

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的标签。在这种情况下,通过将其框架设置为0,0来隐藏NavigationLink,这是一种技巧,但我目前不知道更好的方法。 .hidden() 没有同样的效果。

抱歉,你是否提到需要更改 SceneDelegate.swift 上的这一行才能使其工作。let contentView = NavigationTest().environmentObject(MyAppEnvironmentData())。FYI - user3069232

2
你可以像下面这样做,基于这个回答(它被打包成一个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())

虽然这样做可以达到效果,但是从架构的角度来看,你正在试图将“命令式编程”范例强行推入SwiftUI的反应式逻辑中。我的意思是,我宁愿使用登录逻辑包装在一个ObjectBinding类中,并公开一个isLoggedin属性,使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")
            }
        }
    }
}


0
我在登录转换中使用了一个布尔状态,它看起来非常流畅。
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)
            }
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接