在回调中以编程方式推送视图,SwiftUI
Push View programmatically in callback, SwiftUI
在我看来,Apple 是在鼓励我们放弃在 SwiftUI 中使用 UIViewController
,但不使用视图控制器,我觉得有点力不从心。我想要的是能够实现某种 ViewModel
,它将向 View
.
发出事件
ViewModel:
public protocol LoginViewModel: ViewModel {
var onError: PassthroughSubject<Error, Never> { get }
var onSuccessLogin: PassthroughSubject<Void, Never> { get }
}
查看:
public struct LoginView: View {
fileprivate let viewModel: LoginViewModel
public init(viewModel: LoginViewModel) {
self.viewModel = viewModel
}
public var body: some View {
NavigationView {
MasterView()
.onReceive(self.viewModel.onError, perform: self.handleError)
.onReceive(self.viewModel.onSuccessLogin, perform: self.handleSuccessfullLogin)
}
}
func handleSuccessfullLogin() {
//push next screen
}
func handleError(_ error: Error) {
//show alert
}
}
使用SwiftUI,登录成功不知道怎么推送另一个controller
此外,对于如何以更好的方式实现我想要的任何建议,我将不胜感激。谢谢。
从 beta 5 开始,NavigationLink 是用于以编程方式推送视图的机制。你可以看到它的一个例子 .
我找到了答案。如果你想显示回调的另一个视图,你应该
创建状态@State var pushActive = false
当ViewModel通知登录成功时设置pushActive
为true
func handleSuccessfullLogin() {
self.pushActive = true
print("handleSuccessfullLogin")
}
创建隐藏 NavigationLink
并绑定到该状态
NavigationLink(destination:
ProfileView(viewModel: ProfileViewModelImpl()),
isActive: self.$pushActive) {
EmptyView()
}.hidden()
解决方法,而不创建额外 个空视图。
您可以使用 .disabled(true)
或 .allowsHitTesting(false)
修饰符来禁用 NavigationLink 上的点击。
Disadvantage: You loose default button tap highlighting.
NavigationLink(destination: EnterVerificationCodeScreen(), isActive: self.$viewModel.verifyPinIsShowing) {
Text("Create an account")
}
.allowsHitTesting(false) // or .disabled(true)
.buttonStyle(ShadowRadiusButtonStyle(type: .dark, height: 38))
正如@Bhodan 提到的,您可以通过更改状态来做到这一点
在 Swift 中使用 EnvironmentObjectUI
- 添加用户数据 ObservableObject :
class UserData: ObservableObject, Identifiable {
let id = UUID()
@Published var firebase_uid: String = ""
@Published var name: String = ""
@Published var email: String = ""
@Published var loggedIn: Bool = false
}
loggedIn
属性 将用于监视用户登录或注销的变化
- 现在将其作为
@EnvironmentObject
添加到 Xcode 中的 SceneDelegate.swift
文件中
这只是让它在您的应用程序的任何地方都可以访问
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
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).
// Create the SwiftUI view that provides the window contents.
let userData = UserData()
let contentView = ContentView().environmentObject(userData)
// Use a UIHostingController as window root view controller.
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
window.rootViewController = UIHostingController(rootView: contentView)
self.window = window
window.makeKeyAndVisible()
}
}
一旦您对 loggedIn
属性 进行任何更改,绑定到它的任何 UI 都会响应 true/false 值更改
正如@Bhodan 提到的那样,只需将其添加到您的视图中,它就会响应该更改
struct LoginView: View {
@EnvironmentObject var userData: UserData
var body: some View {
NavigationView {
VStack {
NavigationLink(destination: ProfileView(), isActive: self.$userData.loggedin) {
EmptyView()
}.hidden()
}
}
}
}
我在这里添加了一些片段,因为我认为它简化了一些事情并使重用导航链接更容易:
1.添加视图导航扩展
extension View {
func navigatePush(whenTrue toggle: Binding<Bool>) -> some View {
NavigationLink(
destination: self,
isActive: toggle
) { EmptyView() }
}
func navigatePush<H: Hashable>(when binding: Binding<H>,
matches: H) -> some View {
NavigationLink(
destination: self,
tag: matches,
selection: Binding<H?>(binding)
) { EmptyView() }
}
func navigatePush<H: Hashable>(when binding: Binding<H?>,
matches: H) -> some View {
NavigationLink(
destination: self,
tag: matches,
selection: binding
) { EmptyView() }
}
}
现在,您可以调用任何视图(确保它们(或 parent)在导航视图中)
2。闲来无事
struct Example: View {
@State var toggle = false
@State var tag = 0
var body: some View {
NavigationView {
VStack(alignment: .center, spacing: 24) {
Text("toggle pushed me")
.navigatePush(whenTrue: $toggle)
Text("tag pushed me (2)")
.navigatePush(when: $tag, matches: 2)
Text("tag pushed me (4)")
.navigatePush(when: $tag, matches: 4)
Button("toggle") {
self.toggle = true
}
Button("set tag 2") {
self.tag = 2
}
Button("set tag 4") {
self.tag = 4
}
}
}
}
}
CleanUI 非常简单。
import SwiftUI
import CleanUI
struct ContentView: View {
var body: some View {
NavigationView {
Button(action: {
CUNavigation.pushToSwiftUiView(YOUR_VIEW_HERE)
}){
Text("Push To SwiftUI View")
}
}
}
}
在我看来,Apple 是在鼓励我们放弃在 SwiftUI 中使用 UIViewController
,但不使用视图控制器,我觉得有点力不从心。我想要的是能够实现某种 ViewModel
,它将向 View
.
ViewModel:
public protocol LoginViewModel: ViewModel {
var onError: PassthroughSubject<Error, Never> { get }
var onSuccessLogin: PassthroughSubject<Void, Never> { get }
}
查看:
public struct LoginView: View {
fileprivate let viewModel: LoginViewModel
public init(viewModel: LoginViewModel) {
self.viewModel = viewModel
}
public var body: some View {
NavigationView {
MasterView()
.onReceive(self.viewModel.onError, perform: self.handleError)
.onReceive(self.viewModel.onSuccessLogin, perform: self.handleSuccessfullLogin)
}
}
func handleSuccessfullLogin() {
//push next screen
}
func handleError(_ error: Error) {
//show alert
}
}
使用SwiftUI,登录成功不知道怎么推送另一个controller
此外,对于如何以更好的方式实现我想要的任何建议,我将不胜感激。谢谢。
从 beta 5 开始,NavigationLink 是用于以编程方式推送视图的机制。你可以看到它的一个例子
我找到了答案。如果你想显示回调的另一个视图,你应该
创建状态
@State var pushActive = false
当ViewModel通知登录成功时设置
pushActive
为true
func handleSuccessfullLogin() { self.pushActive = true print("handleSuccessfullLogin") }
创建隐藏
NavigationLink
并绑定到该状态NavigationLink(destination: ProfileView(viewModel: ProfileViewModelImpl()), isActive: self.$pushActive) { EmptyView() }.hidden()
解决方法,而不创建额外 个空视图。
您可以使用 .disabled(true)
或 .allowsHitTesting(false)
修饰符来禁用 NavigationLink 上的点击。
Disadvantage: You loose default button tap highlighting.
NavigationLink(destination: EnterVerificationCodeScreen(), isActive: self.$viewModel.verifyPinIsShowing) {
Text("Create an account")
}
.allowsHitTesting(false) // or .disabled(true)
.buttonStyle(ShadowRadiusButtonStyle(type: .dark, height: 38))
正如@Bhodan 提到的,您可以通过更改状态来做到这一点
在 Swift 中使用 EnvironmentObjectUI
- 添加用户数据 ObservableObject :
class UserData: ObservableObject, Identifiable {
let id = UUID()
@Published var firebase_uid: String = ""
@Published var name: String = ""
@Published var email: String = ""
@Published var loggedIn: Bool = false
}
loggedIn
属性 将用于监视用户登录或注销的变化
- 现在将其作为
@EnvironmentObject
添加到 Xcode 中的SceneDelegate.swift
文件中 这只是让它在您的应用程序的任何地方都可以访问
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
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).
// Create the SwiftUI view that provides the window contents.
let userData = UserData()
let contentView = ContentView().environmentObject(userData)
// Use a UIHostingController as window root view controller.
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
window.rootViewController = UIHostingController(rootView: contentView)
self.window = window
window.makeKeyAndVisible()
}
}
一旦您对 loggedIn
属性 进行任何更改,绑定到它的任何 UI 都会响应 true/false 值更改
正如@Bhodan 提到的那样,只需将其添加到您的视图中,它就会响应该更改
struct LoginView: View {
@EnvironmentObject var userData: UserData
var body: some View {
NavigationView {
VStack {
NavigationLink(destination: ProfileView(), isActive: self.$userData.loggedin) {
EmptyView()
}.hidden()
}
}
}
}
我在这里添加了一些片段,因为我认为它简化了一些事情并使重用导航链接更容易:
1.添加视图导航扩展
extension View {
func navigatePush(whenTrue toggle: Binding<Bool>) -> some View {
NavigationLink(
destination: self,
isActive: toggle
) { EmptyView() }
}
func navigatePush<H: Hashable>(when binding: Binding<H>,
matches: H) -> some View {
NavigationLink(
destination: self,
tag: matches,
selection: Binding<H?>(binding)
) { EmptyView() }
}
func navigatePush<H: Hashable>(when binding: Binding<H?>,
matches: H) -> some View {
NavigationLink(
destination: self,
tag: matches,
selection: binding
) { EmptyView() }
}
}
现在,您可以调用任何视图(确保它们(或 parent)在导航视图中)
2。闲来无事
struct Example: View {
@State var toggle = false
@State var tag = 0
var body: some View {
NavigationView {
VStack(alignment: .center, spacing: 24) {
Text("toggle pushed me")
.navigatePush(whenTrue: $toggle)
Text("tag pushed me (2)")
.navigatePush(when: $tag, matches: 2)
Text("tag pushed me (4)")
.navigatePush(when: $tag, matches: 4)
Button("toggle") {
self.toggle = true
}
Button("set tag 2") {
self.tag = 2
}
Button("set tag 4") {
self.tag = 4
}
}
}
}
}
CleanUI 非常简单。
import SwiftUI
import CleanUI
struct ContentView: View {
var body: some View {
NavigationView {
Button(action: {
CUNavigation.pushToSwiftUiView(YOUR_VIEW_HERE)
}){
Text("Push To SwiftUI View")
}
}
}
}