【问题标题】:SwiftUi - Changing Views Via a functionSwiftUi - 通过函数更改视图
【发布时间】:2021-07-16 10:15:52
【问题描述】:

我正在努力让一些基本代码正常工作,以使我能够更改 Swiftui 项目中的视图。

我有 3 个视图:我的默认 ContentView、一个登录屏幕和一个主菜单。

项目加载到只是一个徽标的 ContentView。我有一个默认为 false 的布尔值和一个加载登录屏幕或主菜单的扩展函数,具体取决于该布尔值。

这部分工作正常,项目加载,我看到登录页面。登录按钮调用一个执行 URLsession 的函数,并根据返回的值将布尔标志设置为 true 或在登录失败的情况下将其保留为 false。

我正在努力获得改变视图的功能。我可以很好地切换函数中的布尔标志,但是如果我包含诸如 MainMenu() 之类的语句来加载我的主菜单视图,则什么也不会发生。

我已经尝试过使用可观察对象和“订阅者”来尝试使其正常工作,但我不确定这是否真的需要,而且我不喜欢让它工作。

非常感谢任何帮助

完整代码:

import SwiftUI
 
var isLoggedin = false
var authenticationFailure = false
 
    func DoLogin(username: inout String, password: inout String){
 
 
        print(isLoggedin)
 
        let url = URL(string: "https://www.example.com/mobile/ios/test.php")!
        var request = URLRequest(url: url)
        request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
        request.httpMethod = "POST"
        let parameters: [String: Any] = [
            "username": username,
            "password": password]
 
 
        request.httpBody = parameters.percentEncoded()
 
        let task = URLSession.shared.dataTask(with: request) { data,response,error in
            guard let data = data,
                  let response = response as? HTTPURLResponse,
                  error == nil else{
                print("error", error ?? "Unknown error")
                return
            }
 
            guard (200 ... 299) ~= response.statusCode else {
                print("statuscode should be 2xx, got \(response.statusCode)")
                print("response = \(response)")
                return
            }
 
            let responseString = String(data: data, encoding: .utf8)
            if responseString == "1"{
                print("Logged in")
                isLoggedin = true
                print(isLoggedin)
                MainMenu()
            }
            else{
                print("NO LOGIN")
                isLoggedin = false
            }
        }
        task.resume()
    }
 
extension View {
  @ViewBuilder func changeView(_ isLoggedin: Bool) -> some View {
    switch isLoggedin {
    case false: LoginView()
    case true: MainMenu()
    }
  }
}
 
struct ContentView: View {
 
    @State var isLoggedin = false
 
    var body: some View {
        Color.clear
        .changeView(isLoggedin)
        VStack{
            Image("logo")
                .padding(.bottom, 40)
 
      }
 }
 
}
 
struct LoginView: View {
 
    @State var username: String = ""
    @State var password: String = ""
 
    @State var isLoggedin = false
 
    var body: some View {
 
 
 
        VStack{
 
            Form{
            TextField("Username: ", text:$username)
                .frame(maxWidth: .infinity, alignment: .center)
                .autocapitalization(.none)
            SecureField("Password: ",text:$password)
            Button("Login"){
                DoLogin(username: &username, password: &password)
                }
 
 
            }
 
        .padding(.top, 100)
 
        }
 
 
    }
 
 
}
 
 
 
 
struct MainMenu: View{
 
    @State var isLoggedin = true
 
    var body: some View{
            VStack{
                    Text("Main Menu")
 
            }
        }
 
    }
 
/*struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        /*ContentView() */
    }
}*/

【问题讨论】:

    标签: swiftui


    【解决方案1】:

    你的代码有问题。

    在您的内容视图中

    @State var isLoggedin = false
    

    没有被结构体内部的任何东西改变,所以它总是会是假的。

    您的 LoginView 调用 doLogin 但它不会更改视图用于呈现自身的任何变量。在您的 doLogin 方法的主体中,它返回视图,但不会将它们返回到任何东西。

    这是一个可以满足您的需求的示例。根据状态显示不同的屏幕。 SwiftUI 根据状态显示视图,因此您需要更改状态以显示不同的视图。我已在一个文件中完成此操作,因此更容易在此处显示:

    import SwiftUI
    
    class ContentViewModel: ObservableObject {
        enum ViewState {
            case initial
            case loading
            case login
            case menu
        }
        @Published var username = ""
        @Published var password = ""
        @Published var viewState = ViewState.initial
    
        var loginButtonDisabled: Bool {
            username.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty  ||
                password.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
        }
    
        func goToLogin() {
            viewState = .login
        }
    
        func login() {
            viewState = .loading
            // I'm not actually logging in, just randomly simulating either a successful or unsuccessful login after a short delay
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
                if Bool.random() {
                    self.viewState = .menu
                } else {
                    self.viewState = .login
                }
            }
        }
    }
    
    struct ContentView: View {
        @StateObject var viewModel = ContentViewModel()
    
        var body: some View {
            ZStack {
                initialView
                loginView
                loadingView
                menuView
            }
        }
    
        private var initialView: InitialView? {
            guard .initial ==  viewModel.viewState else { return nil }
            return InitialView(viewModel: viewModel)
        }
    
        private var loginView: LoginView? {
            guard .login == viewModel.viewState else { return nil }
            return LoginView(viewModel: viewModel)
        }
    
        private var loadingView: LoadingView? {
            guard .loading == viewModel.viewState else { return nil }
            return LoadingView()
        }
    
        private var menuView: MenuView? {
            guard .menu == viewModel.viewState else { return nil }
            return MenuView()
        }
    
    }
    
    struct InitialView: View {
        @ObservedObject var viewModel: ContentViewModel
        var body: some View {
            VStack {
                Text("Initial View")
                    .font(.largeTitle)
                    .padding()
                Button("Login") { viewModel.goToLogin() }
    
            }
        }
    }
    
    struct LoginView: View {
        @ObservedObject var viewModel: ContentViewModel
        var body: some View {
            VStack {
                Text("Login View")
                    .font(.largeTitle)
                    .padding()
                TextField("Username", text: $viewModel.username)
                    .padding()
                TextField("Password", text: $viewModel.password)
                    .padding()
                Button("Login") {viewModel.login() }
                    .padding()
                    .disabled(viewModel.loginButtonDisabled)
            }
        }
    }
    
    struct LoadingView: View {
        var body: some View {
            Text("Loading View")
                .font(.largeTitle)
        }
    }
    
    struct MenuView: View {
        var body: some View {
            Text("Menu View")
                .font(.largeTitle)
        }
    }
    
    struct ContentView_Previews: PreviewProvider {
        static var previews: some View {
            ContentView()
        }
    }
    

    这都是由一个视图模型驱动的,该模型发布了一个枚举状态,ContentView 使用它来显示不同的视图。这是可能的,因为在组中(例如 ZStack),不会呈现 nil 视图。

    你可以从https://github.com/Abizern/SO-68407322克隆一个项目

    【讨论】:

    • 非常感谢您如此详细的回复,不胜感激。在过去的几天里,我一直在兜圈子,试图让它发挥作用。我必须承认我对 SwiftUi 很陌生,我一直在尝试应用其他编码语言的原则但没有成功,但是你的代码对我帮助很大,尤其是在理解我之前哪里出错了。
    • NP,在切换到 SwiftUI 时可能会改变思维方式
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-10-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-03-10
    相关资源
    最近更新 更多