You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SwiftUI中fullScreenCover弹出空白视图问题求助

问题描述

我正在开发首个基于SwiftUI的全栈应用,尝试从处理用户登录的LoginView跳转至处理新用户数据输入的DataInputView,但弹出的fullScreenCover完全空白。我是Swift新手,代码可能存在不规范之处。已排查并发问题、Firebase数据存储及认证功能均正常,无报错信息,但无法解决该问题。

相关代码:

// UserMetaDataViewModel.swift
import Foundation
import FirebaseAuth
import FirebaseFirestore
import UIKit
import FirebaseStorage

@MainActor
class UserMetaDataViewModel: ObservableObject, Identifiable {
    
    @Published var userMetaData: UserMetaData
    
    init(userMetaData: UserMetaData = UserMetaData(id: "", email: "", nameFirst: "", nameLast: "", handle: "", gender: Gender.other, age: 0, bio: "", dataInputComplete: false)) {
        self.userMetaData = userMetaData
    }
    
    func saveUserData(userMetaData: UserMetaData) async -> String? {
        let db = Firestore.firestore()

        if let id = userMetaData.id {
            do {
                try await db.collection("userMetaData").document(id).setData(userMetaData.dictionary)
                print("Success in saving data for user \(userMetaData.nameFirst)")
                return nil
            } catch {
                print("Error could not update data in 'userMetaData'")
                return error.localizedDescription
            }
        } else {
            print("Error could not create a new instance of UserMetaData: UID is nil")
            return "User ID is nil"
        }
    }

    func loadUserData() async -> Bool {
        let db = Firestore.firestore()

        guard let userID = Auth.auth().currentUser?.uid else { return false }

        do {
            let document = try await db.collection("userMetaData").document(userID).getDocument()
            print("Document data: \(String(describing: document.data()))")
            
            if document.exists {
                do {
                    let loadedUserData = try document.data(as: UserMetaData.self)
                    self.userMetaData = loadedUserData
                    return true
                } catch {
                    print("Error decoding user data: \(error)")
                    return false
                }
            } else {
                print("Document not found")
                return false
            }
        } catch {
            print("Error loading user data: \(error.localizedDescription)")
            return false
        }
    }
    
    func saveProfilePic(userMetaData: UserMetaData, image: UIImage) async -> Bool {
        guard let userEmail = userMetaData.email else {
            print("error finding ID of userMetaData")
            return false
        }
        let photoName = UUID().uuidString
        let storage = Storage.storage()
        let storageRef = storage.reference().child("\(userEmail)/\(photoName).jpeg")
        
        guard let resizedImage = image.jpegData(compressionQuality: 0.2) else {
            print("Error, could not resize image")
            return false
        }
        let metadata = StorageMetadata()
        metadata.contentType = "image/jpg"
        
        var imageURLString = ""
        do {
            let _ = try await storageRef.putDataAsync(resizedImage, metadata: metadata)
            do {
                let imageURL = try await storageRef.downloadURL()
                imageURLString = "\(imageURL)"
            } catch {
                print("Error: could not get ImageUrl after saving profile URL")
            }
            print("profile image saved")
        } catch {
            print("Error uploading image to firebase storage")
            return false
        }
        let db = Firestore.firestore()
        let collectionString = "email/\(userEmail)/profilePic"
        
        return true
    }
}

// DataInputView.swift
import SwiftUI

struct DataInputView: View {
    @ObservedObject var userMetaDataVM: UserMetaDataViewModel

    var body: some View {
        Form {
            Section(header: Text("Enter Information")) {
                TextField("First", text: $userMetaDataVM.userMetaData.nameFirst)
                TextField("First", text: $userMetaDataVM.userMetaData.nameLast)
                TextField("Handle", text: $userMetaDataVM.userMetaData.handle)
                Picker("Gender", selection: $userMetaDataVM.userMetaData.gender) {
                    ForEach(Gender.allCases, id: \.self) {
                        Text($0.rawValue)
                    }
                }
                TextField("Age", value: $userMetaDataVM.userMetaData.age, formatter: NumberFormatter())
                TextField("Bio", text: $userMetaDataVM.userMetaData.bio)
            }

            Button(action: {
                Task {
                    if let email = userMetaDataVM.userMetaData.email {
                        _ = await userMetaDataVM.saveUserData(userMetaData: userMetaDataVM.userMetaData)
                    }
                }
            }) {
                Text("Continue")
            }
        }
    }
}

struct SwiftUIView_Previews: PreviewProvider {
    static var previews: some View {
        DataInputView(userMetaDataVM: UserMetaDataViewModel(userMetaData: UserMetaData(id: "", email: "", nameFirst: "", nameLast: "", handle: "", gender: Gender(rawValue: "") ?? Gender.male, age: 10, bio: "", profilePictureURL: "", dataInputComplete: false)))
    }
}
解决方向

1. 修正fullScreenCover的触发与ViewModel传递

父视图LoginView中需用@StateObject持有ViewModel实例,确保生命周期正确,再传递给DataInputView:

struct LoginView: View {
    @State private var showDataInput = false
    @StateObject private var metaDataVM = UserMetaDataViewModel()

    var body: some View {
        Button("登录") {
            // 登录逻辑完成后触发
            showDataInput = true
        }
        .fullScreenCover(isPresented: $showDataInput) {
            DataInputView(userMetaDataVM: metaDataVM)
        }
    }
}

2. 删除重复的ViewModel定义

你提供的代码里重复定义了UserMetaDataViewModel,这会导致编译异常或逻辑混乱,必须删除其中一份。

3. 确保UserMetaData属性为可变类型

检查UserMetaData结构体,所有需要绑定的属性必须是var而非let:

struct UserMetaData: Identifiable, Codable {
    var id: String
    var email: String?
    var nameFirst: String
    var nameLast: String
    var handle: String
    var gender: Gender
    var age: Int
    var bio: String
    var profilePictureURL: String
    var dataInputComplete: Bool

    // 编码为字典的逻辑
    var dictionary: [String: Any] {
        return [
            "id": id,
            "email": email ?? "",
            "nameFirst": nameFirst,
            "nameLast": nameLast,
            "handle": handle,
            "gender": gender.rawValue,
            "age": age,
            "bio": bio,
            "profilePictureURL": profilePictureURL,
            "dataInputComplete": dataInputComplete
        ]
    }
}

4. 完善Gender枚举实现

确保枚举遵循CaseIterable和RawRepresentable,保证Picker正常渲染:

enum Gender: String, CaseIterable {
    case male = "Male"
    case female = "Female"
    case other = "Other"
}

5. 调试视图渲染

在DataInputView中添加测试文本,确认视图是否被加载:

struct DataInputView: View {
    @ObservedObject var userMetaDataVM: UserMetaDataViewModel

    var body: some View {
        VStack {
            Text("数据输入页")
            Form {
                // 原有表单内容
            }
        }
    }
}

如果测试文本显示,问题出在表单绑定逻辑;若仍空白,需检查ViewModel传递或fullScreenCover触发逻辑。

内容的提问来源于stack exchange,提问作者Dune Guy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 19:15:07