【问题标题】:Black screen on iOS Simulator when creating ViewModel创建 ViewModel 时 iOS 模拟器上的黑屏
【发布时间】:2022-08-19 07:18:43
【问题描述】:

当我应该创建我的ProfileViewModel 时,我遇到了奇怪的错误。我将 ViewModel 创建为@StateObject,然后通过.environmentObject() 将其传递给其他视图。不幸的是,当我启动模拟器时,我只有在创建这个特定的 ViewModel 时才会出现黑屏。当我评论创建它的行时,我会在屏幕上看到内容。

我在这里创建它:

@StateObject private var profileViewModel = ProfileViewModel()

我在这里将它传递给其他视图:

case .profile:
    withAnimation(.linear) {
        ProfileView()
           .environmentObject(authStateManager)
           .environmentObject(tabBarStateManager)
           .environmentObject(profileViewModel)
    }

我的ProfileViewModel 看起来像这样:

import UIKit

class ProfileViewModel: ObservableObject {
    @Published var profile: Profile = Profile.demoProfile
    @Published var orders: [Order] = Order.demoOrders
    @Published var returns: [Return] = Return.demoReturns
    
    @Published var oldImage = UIImage(named: \"blank_profile_image\")!
    @Published var image = UIImage(named: \"blank_profile_image\")!
    
    @Published var shouldPresentOrderRateView: Bool = false
    @Published var shouldPresentReturnCreationView: Bool = false
    
    var datesForOrdersViewListSections: [String] {
        var ordersShortDates: [String] = []
        for order in orders {
            ordersShortDates.append(Date.getMonthNameAndYearFrom(date: order.orderDate))
        }
        return ordersShortDates.uniqued().sorted { firstDate, secondDate in
            firstDate.suffix(4) > secondDate.suffix(4)
        }
    }
    
    var datesForReturnsViewListSections: [String] {
        var returnsShortDates: [String] = []
        for userReturn in returns {
            returnsShortDates.append(Date.getMonthNameAndYearFrom(date: userReturn.returnDate))
        }
        return returnsShortDates.uniqued().sorted { firstDate, secondDate in
            firstDate.suffix(4) > secondDate.suffix(4)
        }
    }

    func uploadPhoto() {
        if !image.isEqual(oldImage) {
            oldImage = image
        }
    }
    
    func getOrdersFor(date: String) -> [Order] {
        return orders.filter {
            Date.getMonthNameAndYearFrom(date: $0.orderDate) == date
        }
    }
    
    func getReturnsFor(date: String) -> [Return] {
        return returns.filter {
            Date.getMonthNameAndYearFrom(date: $0.returnDate) == date
        }
    }
    
    func changeDefaultAddress(address: Address) {
        removeAddress(address: address)
        profile.otherAddresses.append(profile.address)
        profile.address = address
    }
    
    func removeAddress(address: Address) {
        for (index, otherAddress) in profile.otherAddresses.enumerated() {
            if otherAddress == address {
                profile.otherAddresses.remove(at: index)
                break
            }
        }
    }
    
    func editPersonalData(firstName: String = \"\", lastName: String = \"\", emailAddress: String = \"\") {
        if !firstName.isEmpty {
            profile.firstName = firstName
        }
        if !lastName.isEmpty {
            profile.lastName = lastName
        }
        if !emailAddress.isEmpty {
            profile.email = emailAddress
        }
    }
    
    func addNewAddress(address: Address, toBeDefault: Bool = false) {
        if toBeDefault {
            profile.otherAddresses.append(profile.address)
            profile.address = address
        } else {
            profile.otherAddresses.append(address)
        }
    }
    
    func editCardData(cardNumber: String, validThru: String, cardholderName: String) {
        if profile.creditCard != nil {
            profile.creditCard!.cardNumber = cardNumber
            profile.creditCard!.validThru = validThru
            profile.creditCard!.cardholderName = cardholderName
        }
    }
    
    func addNewCard(card: CreditCard) {
        profile.creditCard = card
    }
    
    func changeDefaultPaymentMethod(newDefaultPaymentMethod: PaymentMethod) {
        profile.defaultPaymentMethod = newDefaultPaymentMethod
    }
    
    func addUserRating(productID: String, rating: Int, review: String?) {
        profile.addRatingFor(productID: productID, rating: rating, review: review)
    }
}

老实说,我不知道为什么会这样。直到某个时刻一切正常。我对未连接到 ViewModel 的逻辑进行了一些更改,并从xcode package manager 安装了KingFisher package。然后我卸载了它,因为我不再需要它了。一切都在卸载之前开始。从那时起,我无法链接任何可能导致它的行为,也没有对ProfileViewModel 进行任何更改。

我已经尝试过:

  1. 重新启动 xcode 和我的 mac
  2. 清理派生数据文件夹
  3. 丢弃(使用 GIT)我对代码所做的任何更改

    Xcode 控制台没有显示输出,没有错误,一切正常

    实际上我现在发现我的应用程序不想显示任何内容,因为ProfileViewModel 的两个属性:

    @Published var orders: [Order] = Order.demoOrders
    @Published var returns: [Return] = Return.demoReturns
    

    当这两个结构都被注释掉时,一切都按预期工作。

    上述结构: Order

    import Foundation
    
    struct Order {
        var id: String = UUID().uuidString
        var orderDate: Date = Date()
        var estimatedDeliveryDate: Date
        var client: Profile
        var shoppingCart: Cart
        var shippingMethod: ShippingMethod
        var shippingAddress: Address
        var paymentMethod: PaymentMethod = .creditCard
        var invoice: Bool
        var totalCost: Double
        var status: OrderStatus = .placed
        
        init(client: Profile, shoppingCart: Cart, shippingMethod: ShippingMethod, shippingAddress: Address, paymentMethod: PaymentMethod = .creditCard, invoice: Bool = false) {
            self.client = client
            self.shoppingCart = shoppingCart
            self.shippingMethod = shippingMethod
            self.shippingAddress = shippingAddress
            self.paymentMethod = paymentMethod
            self.invoice = invoice
            
            self.estimatedDeliveryDate = calculateEstimatedDeliveryDate(orderDate: Date())
            
            self.totalCost = shoppingCart.products.keys.map { $0.price }.reduce(0, +)
        }
    }
    
    extension Order: Equatable, Hashable {
        static func == (lhs: Order, rhs: Order) -> Bool {
            return lhs.id == rhs.id
        }
        
        func hash(into hasher: inout Hasher) {
            hasher.combine(id)
        }
    }
    
    extension Order: CustomStringConvertible {
        var description: String {
            \"\\(id)\\nOrder Date: \\(Date.getDayMonthYearFrom(date: orderDate))\\nEstimated Delivery Date: \\(Date.getDayMonthYearFrom(date: estimatedDeliveryDate))\\nShipping Method: \\(shippingMethod.rawValue)\\nPayment Method: \\(paymentMethod.rawValue)\\nTotal Cost: \\(totalCost)\\nStatus: \\(status)\"
        }
    }
    
    extension Order {
        static let demoOrders: [Order] = [Order(client: Profile.demoProfile,
                                                shoppingCart: Cart.demoCart,
                                                shippingMethod: .pickup,
                                                shippingAddress: Address.demoAddress),
                                          Order(client: Profile.demoProfile,
                                                shoppingCart: Cart.demoCart,
                                                shippingMethod: .parcel,
                                                shippingAddress: Address.demoAddress),
                                          Order(client: Profile.demoProfile,
                                                shoppingCart: Cart.demoCart,
                                                shippingMethod: .parcel,
                                                shippingAddress: Address.demoAddress),
                                          Order(client: Profile.demoProfile,
                                                shoppingCart: Cart.demoCart,
                                                shippingMethod: .parcel,
                                                shippingAddress: Address.demoAddress)]
    }
    

    Return

    import Foundation
    
    struct Return {
        var id: String = UUID().uuidString
        var returnDate: Date = Date()
        var clientID: String
        var orderID: String
        var products: [Product]
        var returnPrice: Double
        var returnMethod: ShippingMethod
        var status: ReturnStatus = .reported
        
        var bankAccountNumber: String = \"\"
        var bankAccountOwnerName: String = \"\"
        var bankAccountOwnerStreetAndHouseNumber: String = \"\"
        var bankAccountOwnerPostalCode: String = \"\"
        var bankAccountOwnerCity: String = \"\"
        var bankAccountOwnerCountry: String = \"\"
    }
    
    enum ReturnStatus: String {
        case reported = \"Reported\"
        case sent = \"Sent\"
        case delivered = \"Delivered\"
        case moneyReturned = \"Money returned\"
        case closed = \"Closed\"
    }
    
    extension Return: Equatable, Hashable {
        static func == (lhs: Return, rhs: Return) -> Bool {
            return lhs.id == rhs.id
        }
        
        func hash(into hasher: inout Hasher) {
            hasher.combine(id)
        }
    }
    
    extension Return: CustomStringConvertible {
        var description: String {
            \"\\(id)\\nReturn Date: \\(Date.getDayMonthYearFrom(date: returnDate))\\nClient ID: \\(clientID)\\nOrder ID: \\(orderID)\\nReturn Price: \\(returnPrice)\\nReturn Method: \\(returnMethod.rawValue)\\nStatus: \\(status.rawValue)\"
        }
    }
    
    extension Return {
        static let demoReturns: [Return] = [Return(id: UUID().uuidString,
                                                   returnDate: Date(),
                                                   clientID: Profile.demoProfile.id,
                                                   orderID: Order.demoOrders[0].id,
                                                   products: Product.demoProducts,
                                                   returnPrice: Order.demoOrders[0].totalCost,
                                                   returnMethod: Order.demoOrders[0].shippingMethod,
                                                   status: .reported),
                                            Return(id: UUID().uuidString,
                                                   returnDate: Date(),
                                                   clientID: Profile.demoProfile.id,
                                                   orderID: Order.demoOrders[1].id,
                                                   products: Product.demoProducts,
                                                   returnPrice: Order.demoOrders[1].totalCost,
                                                   returnMethod: Order.demoOrders[1].shippingMethod,
                                                   status: .reported)]
    }
    

    我尝试从 Profile、Cart 或 Products 等结构中删除单个属性,但问题仍然存在。

  • 无法判断,但将 View 放入 withAnimation es 肯定不会起作用
  • 您可以尝试在没有私有的情况下声明您的视图模型吗? @StateObject var profileViewModel = ProfileViewModel()
  • 可以尝试不使用withAnimation,接下来尽可能多地注释掉代码并继续尝试直到你找出问题为止,实际上你将归结为一个最小的可重现示例,然后将其发布在这里,这样会更容易测试和工作
  • @tail 删除私人没有帮助。我的问题只是创建profileViewModel。如果我从主视图中注释掉所有视图,只放一些空的Text(\"temp text\"),但仍然创建profileViewModel,则不会显示任何文本,只有黑屏。
  • 我们需要最少的可重现示例来复制粘贴和调试。

标签: swift xcode swiftui viewmodel


【解决方案1】:

这是我见过的使用 Swift 的最奇怪的错误。 当我发现问题与

@Published var orders: [Order] = Order.demoOrders
@Published var returns: [Return] = Return.demoReturns

ProfileViewModel 中构建的结构然后我尝试从orders 开始从头开始创建它们,例如:

    @Published var orders: [Order] = [Order(client: Profile(firstName: "",
                                                           lastName: "",
                                                           username: "",
                                                           birthDate: Date(),
                                                           email: "",
                                                           address: Address(streetName: "",
                                                                            streetNumber: "",
                                                                            apartmentNumber: "",
                                                                            zipCode: "",
                                                                            city: "",
                                                                            country: "")),
                                           shoppingCart: Cart(),
                                           shippingMethod: .courier,
                                           shippingAddress: Address(streetName: "",
                                                                    streetNumber: "",
                                                                    apartmentNumber: "",
                                                                    zipCode: "",
                                                                    city: "",
                                                                    country: ""))]

突然间一切都开始起作用了,所以我又回到:

@Published var orders: [Order] = Order.demoOrders
@Published var returns: [Return] = Return.demoReturns

而且没有黑屏了! 不知道究竟是什么原因造成的。

【讨论】:

    【解决方案2】:

    找到解决方案。事实证明,我用于计算演示订单的预计交货日期的函数之一是在应用程序启动时创建无限循环,因此屏幕上没有显示任何内容,也无法打印任何内容。

    func calculateEstimatedDeliveryDate(orderDate: Date) -> Date {
        var dayComponent = DateComponents()
        dayComponent.day = 2
        var estimatedDeliveryDate = Calendar.current.date(byAdding: dayComponent, to: Date())
        
        dayComponent.day = 1
        while Calendar.current.isDateInWeekend(estimatedDeliveryDate!) {
            estimatedDeliveryDate = Calendar.current.date(byAdding: dayComponent, to: Date())
        }
        
        return estimatedDeliveryDate!
    }
    

    当我通过以下方式创建无限循环时,问题出在这一行:

    estimatedDeliveryDate = Calendar.current.date(byAdding: dayComponent, to: Date())
    

    将一天添加到当前日期并再次检查是否是周末。做得好,应该是:

    estimatedDeliveryDate = Calendar.current.date(byAdding: dayComponent, to: estimatedDeliveryDate!)
    

    第一次问题发生在星期四,一天后消失了,当时可以计算出应用程序不会陷入无限循环 - 它一定是星期五,因为我们正在将 2 天添加到当前日期并检查下一个是否是周末。这不是周末,所以应用程序“自行解锁”。同样的情况是第二次,因为问题发生在星期四晚上,我通过发现它的来源来修复它,而无需等待。

    【讨论】:

      猜你喜欢
      • 2020-06-15
      • 2014-12-03
      • 2014-11-16
      • 1970-01-01
      • 2018-08-29
      • 2021-03-13
      • 1970-01-01
      • 2016-02-10
      • 2020-10-15
      相关资源
      最近更新 更多