Переход к новому представлению в SwftUI после успешной аутентификации из API без кнопки NavigationButton - PullRequest
0 голосов
/ 06 ноября 2019

Я пытаюсь перейти к совершенно новому представлению после получения кода ответа 200 OK от моего API. Код ответа успешно отправляется обратно в приложение и выводится на консоль, и представление загружается. За исключением случаев, когда представление загружается в нагрузках поверх предыдущего представления. Фото того, о чем я говорю . Я не могу использовать навигационную кнопку, потому что у вас не может быть их функций преформ, и я не хочу кнопку возврата в левом верхнем углу экрана после входа в систему. Вот код, который у меня есть сейчас:

    @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()
    }


Я устал гуглить эту проблему, но мне не повезло. Все, что мне нужно, это базовый переход к новому виду без использования кнопки NavigationButton.

Любая помощь приветствуется!

Ответы [ 2 ]

0 голосов
/ 06 ноября 2019

Предполагая, что у вас есть два основных вида (например, LoginView и MainView), вы можете переходить между ними несколькими способами. Что вам понадобится:

  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)
    }
}

Обратите внимание, что в реальной функциональной форме входа в систему вам нужно выполнить базовую очистку ввода и отключить / ограничить скорость кнопкой входа, чтобы не получать миллиард запросов к серверу, если кто-то спамит кнопку.

Для вдохновения см .:
Представление комбайна (сессия WWDC)
Практическое объединение (сессия WWDC)
Использование комбайна (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))
    }
}
0 голосов
/ 06 ноября 2019

Вы можете использовать флаг, чтобы вызвать навигацию, в настоящий момент навигация невозможна без 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
                    }
                }
            }
        }
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...