以编程方式导航到SwiftUI中的新视图

时间:2019-06-25 12:30:39

标签: swift swiftui

说明性示例:

登录屏幕,用户点击“登录”按钮,执行请求,UI显示等待指示器,然后在成功响应后,我想自动将用户导航到下一个屏幕。

如何在SwiftUI中实现这种自动转换?

7 个答案:

答案 0 :(得分:4)

为了将来参考,由于许多用户报告出现错误“函数声明了不透明的返回类型”,因此从@MoRezaFarahani实现上述代码需要以下语法:

std::pair<const char*, T>

这可用于Xcode 11.4和Swift 5

答案 1 :(得分:2)

成功登录后,可以使用登录视图替换下一个视图。例如:

struct LoginView: View {
    var body: some View {
        ...
    }
}

struct NextView: View {
    var body: some View {
        ...
    }
}

// Your starting view
struct ContentView: View {

    @EnvironmentObject var userAuth: UserAuth 

    var body: some View {
        if !userAuth.isLoggedin {
            LoginView()
        } else {
            NextView()
        }

    }
}

您应该在数据模型中处理登录过程,并使用诸如@EnvironmentObject之类的绑定将isLoggedin传递给视图。

  

注意:在Xcode 版本11.0 beta 4 中,要符合协议'BindableObject',必须添加willChange属性

class UserAuth: BindableObject {

  let didChange = PassthroughSubject<UserAuth,Never>()

  // required to conform to protocol 'BindableObject' 
  let willChange = PassthroughSubject<UserAuth,Never>()

  func login() {
    // login request... on success:
    self.isLoggedin = true
  }

  var isLoggedin = false {
    didSet {
      didChange.send(self)
    }

    // willSet {
    //       willChange.send(self)
    // }
  }
}

答案 2 :(得分:2)

struct LoginView: View {
    
    @State var isActive = false
    @State var attemptingLogin = false
    
    var body: some View {
        ZStack {
            NavigationLink(destination: HomePage(), isActive: $isActive) {
                Button(action: {
                    attlempinglogin = true
                    // Your login function will most likely have a closure in 
                    // which you change the state of isActive to true in order 
                    // to trigger a transition
                    loginFunction() { response in
                        if response == .success {
                            self.isActive = true
                        } else {
                            self.attemptingLogin = false
                        }
                    }
                }) {
                    Text("login")
                }
            }
            
            WaitingIndicator()
                .opacity(attemptingLogin ? 1.0 : 0.0)
        }
    }
}

使用导航链接和$ isActive绑定变量

答案 3 :(得分:1)

要说明其他人根据Swift Version 5.2以来的组合更改所做的详细说明,可以使用发布者进行简化。

  1. 创建一个类名UserAuth,如下所示,不要忘记导入import Combine
class UserAuth: ObservableObject {
        @Published var isLoggedin:Bool = false

        func login() {
            self.isLoggedin = true
        }
    }
  1. 使用{p>更新SceneDelegate.Swift

    let contentView = ContentView().environmentObject(UserAuth())

  2. 您的身份验证视图

     struct LoginView: View {
        @EnvironmentObject  var  userAuth: UserAuth
        var body: some View {
            ...
        if ... {
        self.userAuth.login()
        } else {
        ...
        }
     }
    }
    
    
  3. 成功通过身份验证后的仪表板,如果身份验证userAuth.isLoggedin = true将被加载。

       struct NextView: View {
         var body: some View {
         ...
         }
       }
    
  4. 最后,启动应用程序后要加载的初始视图。

struct ContentView: View {
    @EnvironmentObject var userAuth: UserAuth 
    var body: some View {
        if !userAuth.isLoggedin {
                LoginView()
            } else {
                NextView()
            }
    }
  }

答案 4 :(得分:0)

现在,您只需要简单地创建要导航到的新视图的实例,然后将其放入NavigationButton中即可:

NavigationButton(destination: NextView(), isDetail: true, onTrigger: { () -> Bool in
    return self.done
}) {
    Text("Login")
}

如果返回true,则onTrigger表示您已成功登录用户。

答案 5 :(得分:0)

这是UINavigationController上的扩展,可以通过SwiftUI视图进行简单的推入/弹出操作,从而获得正确的动画。我在上面的大多数自定义导航中遇到的问题是推/弹出动画已关闭。将NavigationLinkisActive绑定一起使用是正确的方法,但是它不灵活或不可扩展。所以下面的扩展为我做了把戏:

/**
 * Since SwiftUI doesn't have a scalable programmatic navigation, this could be used as
 * replacement. It just adds push/pop methods that host SwiftUI views in UIHostingController.
 */
extension UINavigationController: UINavigationControllerDelegate {

    convenience init(rootView: AnyView) {
        let hostingView = UIHostingController(rootView: rootView)
        self.init(rootViewController: hostingView)

        // Doing this to hide the nav bar since I am expecting SwiftUI
        // views to be wrapped in NavigationViews in case they need nav.
        self.delegate = self
    }

    public func pushView(view:AnyView) {
        let hostingView = UIHostingController(rootView: view)
        self.pushViewController(hostingView, animated: true)
    }

    public func popView() {
        self.popViewController(animated: true)
    }

    public func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) {
        navigationController.navigationBar.isHidden = true
    }
}

这里是一个简单的示例,将其用于window.rootViewController

var appNavigationController = UINavigationController.init(rootView: rootView)
window.rootViewController = appNavigationController
window.makeKeyAndVisible()

// Now you can use appNavigationController like any UINavigationController, but with SwiftUI views i.e. 
appNavigationController.pushView(view: AnyView(MySwiftUILoginView()))

答案 6 :(得分:0)

我遵循了 Gene 的回答,但我在下面解决了两个问题。第一个是变量 isLoggedIn 必须具有 @Published 属性才能按预期工作。二是如何实际使用环境对象。

首先,将 UserAuth.isLoggedIn 更新为以下内容:

@Published var isLoggedin = false {
didSet {
  didChange.send(self)
}

第二个是如何实际使用环境对象。这在 Gene 的回答中并没有错,我只是在评论中注意到了很多关于它的问题,我没有足够的业力来回应他们。将此添加到您的 SceneDelegate 视图:

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
    // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
    // If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
    // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
    var userAuth = UserAuth()
    
    // Create the SwiftUI view that provides the window contents.
    let contentView = ContentView().environmentObject(userAuth)