在没有NavigationButton的情况下成功通过API进行身份验证后,在SwftUI中选择到新视图

从API获得200 OK响应代码后,我试图选择一个全新的视图。响应代码成功发送回应用程序,并打印到控制台中并加载视图。当视图加载到前一个视图的顶部时除外。 Photo of what I'm talking about。我无法使用导航按钮,因为您无法使用它们的瓶坯功能,并且我也不想在登录后在屏幕的左上方单击“后退”按钮。这是我现在拥有的代码;

    @State var username: String = ""
    @State var password: String = ""
    @State var sayHello = false
    @State private var showingAreaView = false
    @State private var showingAlert = false
    @State private var alertTitle = ""
    @State private var alertMessage = ""

Button(action: {
      self.login()

      })//end of login function
               {
                       Image("StartNow 3").resizable()
                         .scaledToFit()
                        .padding()
                       }
                if showingAreaView == true {
                    AreaView()
                        .animation(.easeIn)
                }


//actual Login func

    func login(){
        let login = self.username
        let passwordstring = self.password
         guard let url = URL(string: "http://localhost:8000/account/auth/") else {return}

         let headers = [
             "Content-Type": "application/x-www-form-urlencoded","cache-control": "no-cache","Postman-Token": "89a81b3d-d5f3-4f82-8b7f-47edc39bb201"
         ]

         let postData = NSMutableData(data: "username=\(login)".data(using: String.Encoding.utf8)!)
         postData.append("&password=\(passwordstring)".data(using: String.Encoding.utf8)!)

         let request = NSMutableURLRequest(url: NSURL(string: "http://localhost:8000/account/auth/")! as URL,cachePolicy:.useProtocolCachePolicy,timeoutInterval: 10.0)
         request.httpMethod = "POST"
         request.allHTTPHeaderFields = headers
         request.httpBody = postData as Data

         let session = URLSession.shared
         let dataTask = session.dataTask(with: request as URLRequest,completionHandler: { (data,response,error) in
             if let httpResponse = response as? HTTPURLResponse {
                 guard let data = data else {return}
                 print(data)
                 if httpResponse.statusCode == 200{
                     DispatchQueue.main.async {
                         //Segue to new view goes here
                         print(httpResponse.statusCode)
                        self.showingAreaView = true

                     }
                 }else{
             if httpResponse.statusCode == 400{
                     DispatchQueue.main.async {
                    self.alertTitle = "Oops"
                    self.alertMessage = "username or Password Incorrect"
                    self.showingAlert = true
                         print(httpResponse.statusCode)

                         }
                     }else{
                         DispatchQueue.main.async {
                            self.alertTitle = "Well Damn"
                            self.alertMessage = "Ay chief we have no idea what just happened but it didn't work"
                            self.showingAlert = true

                         }
                     }
             }
                 do{

                     let JSONFromServer = try JSONSerialization.jsonObject(with: data,options: [])
                     let decoder = JSONDecoder()
                     decoder.keyDecodingStrategy = .convertFromsnakeCase
                     let tokenArray = try decoder.decode(token.self,from: data)
                     print(tokenArray.token)
                     UserDefaults.standard.set(tokenArray.token,forKey: "savedToken")
                     let savedToken = UserDefaults.standard.object(forKey: "savedToken")
                     print(savedToken)
                 }catch{
                     if httpResponse.statusCode == 400{
                    self.alertTitle = "Oops"
                    self.alertMessage = "username or Password Incorrect"
                    self.showingAlert = true

                     }
                     print(error)
                     print(httpResponse.statusCode)
                 }
             } else if let error = error {
                self.alertTitle = "Well Damn"
                self.alertMessage = "Ay chief we have no idea what just happened but it didn't work"
                self.showingAlert = true
                 print(error)
             }
         })
         dataTask.resume()
    }


我已经厌倦了用谷歌搜索这个问题,但是没有任何运气。我所需要的只是一个基本视图,无需导航按钮。

感谢您的帮助!

bitwzj 回答:在没有NavigationButton的情况下成功通过API进行身份验证后,在SwftUI中选择到新视图

您可以使用一个标志来触发导航,目前如果没有NavigationLink

struct ContentView: View {

    @State var logedIn = false

    var body: some View {

        return NavigationView {
            VStack {
                NavigationLink(destination: DestinationView(),isActive: $logedIn,label: { 
                    EmptyView() 
                })// It won't appear on screen because the label is EmptyView
                Button("log in") {
                    // log in logic
                    if succesful login {
                        logedIn = true // this will trigger the NavigationLink
                    }
                }
            }
        }
    }
}
,

假设您有两个基本视图(例如LoginViewMainView),则可以通过两种方式在它们之间进行转换。您需要的是:

  1. 确定要显示哪个状态的某种状态
  2. 某些包装视图将在#1更改时在两种布局之间转换
  3. 以某种方式在视图之间传递数据

在这个答案中,我将把#1和#3合并到一个模型对象中,并显示#2的两个示例。您可以通过多种方法来实现这一目标,因此请尝试一下,看看哪种方法最适合您。

请注意,有很多代码只是用来设置视图的样式,因此您可以看到正在发生的事情。我已经评论了关键点。

图片(左侧为不透明度方法,右侧为偏移方法)

Opacity Transition Offset Transition

模型(满足#1和#3)

class LoginStateModel: ObservableObject {
    // changing this will change the main view
    @Published var loggedIn = false
    // will store the username typed on the LoginView
    @Published var username = ""

    func login() {
        // simulating successful API call
        DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
            // when we log in,animate the result of setting loggedIn to true
            //   (i.e.,animate showing MainView)
            withAnimation(.default) {
                self.loggedIn = true
            }
        }
    }
}

顶级视图(满足#2)

struct ContentView: View {
    @ObservedObject var model = LoginStateModel()

    var body: some View {
        ZStack {
            // just here for background
            Color(UIColor.cyan).opacity(0.3)
                .edgesIgnoringSafeArea(.all)

            // we show either LoginView or MainView depending on our model
            if model.loggedIn {
                MainView()
            } else {
                LoginView()
            }
        }
            // this passes the model down to descendant views
            .environmentObject(model)
    }
}

从视图层次结构中添加视图或从中删除视图的默认过渡是更改其不透明度。由于我们在model.loggedIn中封装了对withAnimation(.default)的更改,因此这种不透明性更改将缓慢发生(在实际设备上比下面的压缩GIF更好)。

或者,我们可以让视图使用偏移量在屏幕上切换,而不是使视图淡入/淡出。对于第二个示例,用

替换上面的if / else块(包括if本身)
MainView()
    .offset(x: model.loggedIn ? 0 : UIScreen.main.bounds.width,y: 0)
LoginView()
    .offset(x: model.loggedIn ? -UIScreen.main.bounds.width : 0,y: 0)

登录视图

struct LoginView: View {
    @EnvironmentObject var model: LoginStateModel

    @State private var usernameString = ""
    @State private var passwordString = ""

    var body: some View {
        VStack(spacing: 15) {
            HStack {
                Text("Username")
                Spacer()
                TextField("Username",text: $usernameString)
                    .textFieldStyle(RoundedBorderTextFieldStyle())
            }
            HStack {
                Text("Password")
                Spacer()
                SecureField("Password",text: $passwordString)
                    .textFieldStyle(RoundedBorderTextFieldStyle())
            }
            Button(action: {
                // save the entered username,and try to log in
                self.model.username = self.usernameString
                self.model.login()
            },label: {
                Text("Login")
                    .font(.title)
                    .inExpandingRectangle(Color.blue.opacity(0.6))
            })
                .buttonStyle(PlainButtonStyle())
        }
        .padding()
        .inExpandingRectangle(Color.gray)
        .frame(width: 300,height: 200)
    }
}

请注意,在真实的功能性登录表单中,您需要进行一些基本的输入操作,并禁用/限制登录按钮的速率,这样,如果有人向该按钮发送垃圾邮件,您就不会收到十亿个服务器请求。

有关灵感的信息,请参见:
Introducing Combine(WWDC会议)
Combine in Practice(WWDC会议)
Using Combine(UIKit示例,但显示了如何限制网络请求)

主视图

struct MainView: View {
    @EnvironmentObject var model: LoginStateModel

    var body: some View {
        VStack(spacing: 15) {
            ZStack {
                Text("Hello \(model.username)!")
                    .font(.title)
                    .inExpandingRectangle(Color.blue.opacity(0.6))
                    .frame(height: 60)

                HStack {
                    Spacer()
                    Button(action: {
                        // when we log out,animate the result of setting loggedIn to false
                        //   (i.e.,animate showing LoginView)
                        withAnimation(.default) {
                            self.model.loggedIn = false
                        }
                    },label: {
                        Text("Logout")
                            .inFittedRectangle(Color.green.opacity(0.6))
                    })
                        .buttonStyle(PlainButtonStyle())
                        .padding()
                }
            }

            Text("Content")
                .inExpandingRectangle(.gray)
        }
        .padding()
    }
}

一些便捷扩展

extension View {
    func inExpandingRectangle(_ color: Color) -> some View {
        ZStack {
            RoundedRectangle(cornerRadius: 15)
                .fill(color)
            self
        }
    }

    func inFittedRectangle(_ color: Color) -> some View {
        self
            .padding(5)
            .background(RoundedRectangle(cornerRadius: 15)
                .fill(color))
    }
}
本文链接:https://www.f2er.com/3155093.html

大家都在问