我正在開發一個新的社交媒體應用程式,并且我的導航代碼有問題。
用戶填寫注冊表單后,我希望提示他們上傳個人資料圖片。我遇到的問題是它顯示了半秒鐘的預期視圖,然后又回到了注冊視圖。
我有一個處理 UI 的 RegistrationView 和一個負責服務器端通信的 AuthViewModel。本質上是當用戶完成輸入資訊并點擊按鈕時。AuthViewModel 接管并將資訊發送到 firebase,然后觸發 Bool 為真。
然后,我在 RegistrationView 上有一個 NagivationLink 來監聽該布林值,當它為真時,會更改 UI 上的視圖。這是代碼。
NavigationLink(destination: ProfilePhotoSelectorView(), isActive: $viewModel.didAuthenticateUser, label:{} )
XCode 吐槽它在 iOS 16 中已被棄用,并轉移到他們開發的 NavigationStack 系統。但是,對于我能看到的每一個指南,我都無法讓它發揮作用。我唯一可以讓它作業的是通過上面的代碼并回傳這個 UI 故障。
這是 RegistrationView 的完整代碼
import SwiftUI
struct RegistrationView: View {
@State private var email = ""
@State private var username = ""
@State private var fullName = ""
@State private var password = ""
@State private var isVerified = false
@Environment(\.presentationMode) var presentationMode
@EnvironmentObject var viewModel: AuthViewModel
var body: some View {
VStack{
NavigationLink(destination: ProfilePhotoSelectorView(), isActive: $viewModel.didAuthenticateUser, label:{} )
AuthHeaderView(title1: "Get Started.", title2: "Create Your Account.")
VStack(spacing: 40) {
CustomInputFields(imageName: "envelope", placeholderText: "Email", isSecureField: false, text: $email)
CustomInputFields(imageName: "person", placeholderText: "Username", isSecureField: false, text: $username)
CustomInputFields(imageName: "person", placeholderText: "Full Name", isSecureField: false, text: $fullName)
CustomInputFields(imageName: "lock", placeholderText: "Password", isSecureField: true, text: $password)
}
.padding(32)
Button {
viewModel.register(withEmail: email, password: password, fullname: fullName, username: username, isVerified: isVerified)
} label: {
Text("Sign Up")
.font(.headline)
.foregroundColor(.white)
.frame(width: 340, height: 50)
.background(Color("AppGreen"))
.clipShape(Capsule())
.padding()
}
.shadow(color: .gray.opacity(0.5), radius: 10, x:0, y:0)
Spacer()
Button {
presentationMode.wrappedValue.dismiss()
} label: {
HStack {
Text("Already Have And Account?")
.font(.caption)
Text("Sign In")
.font(.footnote)
.fontWeight(.semibold)
}
}
.padding(.bottom, 32)
.foregroundColor(Color("AppGreen"))
}
.ignoresSafeArea()
.preferredColorScheme(.dark)
}
}
struct RegistrationView_Previews: PreviewProvider {
static var previews: some View {
RegistrationView()
}
}
這是 AuthViewModel 的完整代碼
import SwiftUI
import Firebase
class AuthViewModel: ObservableObject {
@Published var userSession: Firebase.User?
@Published var didAuthenticateUser = false
init() {
self.userSession = Auth.auth().currentUser
print("DEBUG: User session is \(String(describing: self.userSession?.uid))")
}
func login(withEmail email: String, password: String){
Auth.auth().signIn(withEmail: email, password: password) { result, error in
if let error = error {
print("DEBUG: Failed to sign in with error\(error.localizedDescription)")
return
}
guard let user = result?.user else { return }
self.userSession = user
print("Did log user in")
}
}
func register(withEmail email: String, password: String, fullname: String, username: String, isVerified: Bool){
Auth.auth().createUser(withEmail: email, password: password) { result, error in
if let error = error {
print("DEBUG: Failed to register with error\(error.localizedDescription)")
return
}
guard let user = result?.user else { return }
print("DEBUG: Registerd User Succesfully")
let data = ["email": email, "username" :username.lowercased(), "fullname": fullname, "isVerified": isVerified, "uid": user.uid]
Firestore.firestore().collection("users")
.document(user.uid)
.setData(data) { _ in
self.didAuthenticateUser = true
}
}
}
func signOut() {
userSession = nil
try? Auth.auth().signOut()
}
}
這是 ProfilePhotoSelectorView 的代碼
import SwiftUI
struct ProfilePhotoSelectorView: View {
var body: some View {
VStack {
AuthHeaderView(title1: "Account Creation:", title2: "Add A Profile Picture")
Button {
print("Pick Photo Here")
} label: {
VStack{
Image("PhotoIcon")
.resizable()
.renderingMode(.template)
.frame(width: 180, height: 180)
.scaledToFill()
.padding(.top, 44)
.foregroundColor(Color("AppGreen"))
Text("Tap To Add Photo")
.font(.title3).bold()
.padding(.top, 10)
.foregroundColor(Color("AppGreen"))
}
}
Spacer()
}
.ignoresSafeArea()
.preferredColorScheme(.dark)
}
}
struct ProfilePhotoSelectorView_Previews: PreviewProvider {
static var previews: some View {
ProfilePhotoSelectorView()
}
}
嘗試了新 NavigationStack 的所有變體,并嘗試了其他一些按鈕代碼,看看我是否可以從那里觸發它。無解析度
uj5u.com熱心網友回復:
不推薦以這種方式使用NavigationLink,所以我并不驚訝它會導致錯誤行為。RegistrationView由于您沒有放置在NavigationView(已棄用)或中,因此加劇了這種情況NavigationStack,因為這些視圖提供了導航鏈接的大部分功能。
就像你說的那樣,這種用法已NavigationLink被棄用。在我看來,該isActive屬性一直有點模棱兩可(據我了解,它并不是導航鏈接的“激活器”,而是一種讀取鏈接是否處于活動狀態的方式)。呈現導航鏈接的新方法(使用.navigationDestination)要好得多。
使用布爾屬性呈現視圖
您本質上想要的是ProfilePhotoSelectorView在布爾屬性切換為 true 時呈現。這是 SwiftUi 中的常見范例,有很多方法可以做到這一點,例如.sheet(isPresented:content:)or .popover(isPresented:content:)。請注意isPresented,兩種方法中的引數都是布爾屬性。使用.sheet,例如:
struct RegistrationView: View {
// ...
@EnvironmentObject var viewModel: AuthViewModel
var body: some View {
VStack {
// ...
}
// Presents the photo selector view when `didAuthenticateUser` is true
.sheet(isPresented: $viewModel.didAuthenticateUser) {
ProfilePhotoSelectorView()
}
}
}
向導航樹添加新視圖
如果您堅持使用導航鏈接(即您真的希望ProfilePhotoSelectorView成為導航樹中的一個節點),您將不得不學習使用新NavigationStack的并將視圖附加到路徑上。這將需要一些重組(可能需要您閱讀一些資料;這里和這里都是很好的起點)。視圖模型將是最有可能控制堆疊的地方,盡管您最終可能希望創建一個專用的視圖模型。這是一個簡單的例子:
struct ContentView: View {
@StateObject var viewModel = AuthViewModel()
var body: some View {
NavigationStack(path: $viewModel.navigationPath) {
RegistrationView()
.environmentObject(viewModel)
.navigationDestination(for: RegistrationScreen.self) { screen in
switch screen {
case .photoSelection:
ProfilePhotoSelectorView()
}
}
}
}
}
class AuthViewModel: ObservableObject {
// ...
// A new enum that defines the various types of possible views in the navigation stack
enum RegistrationScreen: Hashable {
case photoSelection
}
// The navigation path
@Published var navigationPath: [RegistrationScreen] = []
// Example usages of the navigation path. These functions show how to programmatically control the navigation stack
func showPhotoSelectionScreen() {
self.navigationPath.append(.photoSelection)
}
func goToRootOfNavigation() {
self.navigationPath = []
}
}
uj5u.com熱心網友回復:
如果我正確理解您的問題,您想使用NavigationStack,但它不適合您。
有很多缺失的部分,但這是我嘗試使用NavigationStack
來觸發目的地,給定viewModel.didAuthenticateUser.
struct ContentView: View {
@StateObject var viewModel = AuthViewModel()
var body: some View {
NavigationStack(path: $viewModel.didAuthenticateUser) { // <-- here
RegistrationView()
.environmentObject(viewModel)
}
}
}
struct RegistrationView: View {
@State private var email = ""
@State private var username = ""
@State private var fullName = ""
@State private var password = ""
@State private var isVerified = false
@Environment(\.presentationMode) var presentationMode
@EnvironmentObject var viewModel: AuthViewModel
var body: some View {
VStack{
AuthHeaderView(title1: "Get Started.", title2: "Create Your Account.")
VStack(spacing: 40) {
CustomInputFields(imageName: "envelope", placeholderText: "Email", isSecureField: false, text: $email)
CustomInputFields(imageName: "person", placeholderText: "Username", isSecureField: false, text: $username)
CustomInputFields(imageName: "person", placeholderText: "Full Name", isSecureField: false, text: $fullName)
CustomInputFields(imageName: "lock", placeholderText: "Password", isSecureField: true, text: $password)
}
.padding(32)
Button {
viewModel.register(withEmail: email, password: password, fullname: fullName, username: username, isVerified: isVerified)
} label: {
Text("Sign Up")
.font(.headline)
.foregroundColor(.white)
.frame(width: 340, height: 50)
.background(Color("AppGreen"))
.clipShape(Capsule())
.padding()
}
.shadow(color: .gray.opacity(0.5), radius: 10, x:0, y:0)
Spacer()
Button {
presentationMode.wrappedValue.dismiss()
} label: {
HStack {
Text("Already Have And Account?")
.font(.caption)
Text("Sign In")
.font(.footnote)
.fontWeight(.semibold)
}
}
.padding(.bottom, 32)
.foregroundColor(Color("AppGreen"))
}
.navigationDestination(for: Bool.self) { _ in // <-- here
ProfilePhotoSelectorView()
}
.ignoresSafeArea()
.preferredColorScheme(.dark)
}
}
class AuthViewModel: ObservableObject {
@Published var userSession: Firebase.User?
@Published var didAuthenticateUser: [Bool] = [] // <-- here
init() {
self.userSession = Auth.auth().currentUser
print("DEBUG: User session is \(String(describing: self.userSession?.uid))")
}
func login(withEmail email: String, password: String){
Auth.auth().signIn(withEmail: email, password: password) { result, error in
if let error = error {
print("DEBUG: Failed to sign in with error\(error.localizedDescription)")
return
}
guard let user = result?.user else { return }
self.userSession = user
print("Did log user in")
}
}
func register(withEmail email: String, password: String, fullname: String, username: String, isVerified: Bool){
Auth.auth().createUser(withEmail: email, password: password) { result, error in
if let error = error {
print("DEBUG: Failed to register with error\(error.localizedDescription)")
return
}
guard let user = result?.user else { return }
print("DEBUG: Registerd User Succesfully")
let data = ["email": email, "username" :username.lowercased(), "fullname": fullname, "isVerified": isVerified, "uid": user.uid]
Firestore.firestore().collection("users")
.document(user.uid)
.setData(data) { _ in
self.didAuthenticateUser = [true] // <-- here
}
}
}
func signOut() {
userSession = nil
try? Auth.auth().signOut()
}
}
struct ProfilePhotoSelectorView: View {
var body: some View {
VStack {
AuthHeaderView(title1: "Account Creation:", title2: "Add A Profile Picture")
Button {
print("Pick Photo Here")
} label: {
VStack{
Image(systemName: "globe")
.resizable()
.renderingMode(.template)
.frame(width: 180, height: 180)
.scaledToFill()
.padding(.top, 44)
.foregroundColor(Color("AppGreen"))
Text("Tap To Add Photo")
.font(.title3).bold()
.padding(.top, 10)
.foregroundColor(Color("AppGreen"))
}
}
Spacer()
}
.ignoresSafeArea()
.preferredColorScheme(.dark)
}
}
uj5u.com熱心網友回復:
NavigationLink除非您將鏈接標記為isDetail(false)或使用,否則您只能擁有一級s .navigationStyle(.stack)。
原因是在橫向中它使用拆分視圖,并且鏈接替換了右側的大細節窗格。
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/531436.html
標籤:Google Cloud Collective 迅速火力基地用户界面迅捷导航
