无法在合理的时间内对该表达式进行类型检查 - ForEach SwiftUI

Posted

技术标签:

【中文标题】无法在合理的时间内对该表达式进行类型检查 - ForEach SwiftUI【英文标题】:Unable to type-check this expression in reasonable time - ForEach SwiftUI 【发布时间】:2020-04-07 16:30:20 【问题描述】:

我有一个结构如下的 JSON 文件:

// MARK: - UcmData
struct UcmData: Codable, Identifiable 
    let id: Int
    let building: [Building]


// MARK: - Building
struct Building: Codable, Identifiable 
    let id: Int
    let title, subtitle, info, image: String
    let floor: [Floor]


// MARK: - Floor
struct Floor: Codable, Identifiable 
    let id, number: Int
    let title, subtitle, image: String
    let cabinet: [Cabinet]?


// MARK: - Cabinet
struct Cabinet: Codable, Identifiable 
    let id: Int
    let number: String
    let person: [Person]


// MARK: - Person
struct Person: Codable, Identifiable 
    let id: Int
    let name: String

而且我需要列出特定建筑物中特定楼层的每个机柜的所有人 - 我在这里尝试这样做:

import SwiftUI

struct FloorDetailedView: View 
    let ucmData = Bundle.main.decode(UcmData.self, from: "ucm_data.json")
    let buildingId: Int?
    let floorId: Int?
    let floorTitle: String?
    let buildingTitle: String?

    init(buildingId: Int? = nil, floorId: Int? = nil, floorTitle: String? = "nil", buildingTitle: String? = "nil") 
        self.buildingId = buildingId
        self.floorId = floorId
        self.floorTitle = floorTitle
        self.buildingTitle = buildingTitle

    

    var body: some View 
        ScrollView 
            VStack 
                ForEach(ucmData.building)  building in
                    if (building.id == self.buildingId) 
                        ForEach(building.floor)  floor in
                            if (floor.id == self.floorId) 
                                ForEach(floor.cabinet)  cabinet in
                                    Image(systemName: "house")
                                        .cornerRadius(40)
                                    VStack(alignment: .leading) 
                                        Text(cabinet.name)
                                        ForEach(cabinet.person)  person in
                                            Text(person.name)
                                                .font(.subheadline)
                                                .color(.gray)
                                        
                                    
                                
                            
                        
                    
                   
            
            .padding(.horizontal)
            .padding(.bottom)
        
        .navigationBarTitle(Text(self.buildingTitle! + " - " + self.floorTitle!), displayMode: .inline)
    

但是,当我将第三个 ForEach 添加到视图代码时,我收到此错误 Unable to type-check this expression in reasonable time。我从前一个视图中得到buildingIdfloorId。什么是更有效的方法,所以我可以过滤橱柜和人员并修复此错误?谢谢。

【问题讨论】:

你添加的指令越多,它消耗的时间就越多......只需在几个子视图上打破那个 VStack 怪物。 但是当我仍然需要过滤槽 building.id 和 floor.id 以在层次结构中找到更深的对象时,这怎么可能?柜子和人? 顺便说一句,我在一个视图中也有一个三重 ForEach,虽然它编译正常,但在各种可重现的情况下,视图没有正确更新。将最后一个 ForEach 分离到另一个 View 中完全解决了这个问题。 【参考方案1】:

编译器无法在合理的时间内对该表达式进行类型检查;尝试将表达式分解为不同的子表达式

SwiftUI 编译器告诉我们将表达式分解为子表达式。 换句话说,我们需要把这个视图分解成更小的子视图。

SwiftUI 的编译器识别出可能导致性能问题的代码时,会弹出此类错误。 在您的情况下,您有一个循环中的循环,这可能是......不好!

话虽如此,SwiftUI 编译器仍在优化以处理像您这样的情况。


现在查看您的实现,从技术上讲它应该可以工作,但必须进行优化。 假设建筑物/楼层/机柜 ID 是唯一的,您的 ForEachif 对建筑物和楼层的 ID 检查只会执行一次。 此外,您似乎只想展示橱柜,为什么不直接这样做呢?

解决办法:

struct FloorDetailedView: View 
  let ucmData: UcmData
  let buildingId: Int?
  let floorId: Int?
  
  var cabinets: [Cabinet]?  //1
    return ucmData
      .building
      .first  $0.id == self.buildingId ?
      .floor
      .first  $0.id == self.floorId ?
      .cabinet
  
  
  var body: some View 
    Group 
      if cabinets != nil  //2
        CabinetView(cabinets: cabinets!) //3
       else 
        Text("No Cabinets")
      
    
  


struct CabinetView: View 
  let cabinets: [Cabinet]
  
  var body: some View 
    List(cabinets)  (cabinet) in
      VStack(alignment: .leading) 
        Image(systemName: "house")

        Text(cabinet.number)

        ForEach(cabinet.person)  (person) in
          Text(person.name)
        
      
    
  

    我们通过使用buildingIdfloorId 使计算属性cabinets 通过ucmData 降低了复杂性 我们检查是否有橱柜可供展示。如果没有可用的橱柜,我们只会显示一条消息 CabinetView 被分离出来

【讨论】:

这是一个很好的解决方案,我不知道我可以像这样跳过检查建筑物和地板,谢谢! @redearz 很高兴能提供帮助 :) 好吧......自上而下的方法通常会受到清晰的影响,因为我们必须考虑多个事情。自下而上的方法使您可以专注于手头的任务。假设你有你需要的东西,处理它,然后弄清楚如何提供这个对象。【参考方案2】:

我修复了这个问题,创建了 2 个单独的视图:

import SwiftUI

struct CabinetView: View 
    let floor: Floor?
    let floorId: Int?

    init(floor: Floor? = nil, floorId: Int? = nil) 
        self.floor = floor
        self.floorId = floorId
    

    var body: some View 
        ForEach(floor!.cabinet!)  cabinet in
            Group 
                HStack 
                     Image("avatar")
                        .resizable()
                        .frame(width: 40, height: 40)
                        .border(Color.gray.opacity(0.5), width: 0.5)
                        .cornerRadius(40/2)
                    VStack(alignment: .leading, spacing: 3) 
                        Text(cabinet.number)
                        ForEach(cabinet.person)  person in
                            Text(person.name)
                                .font(.subheadline)
                        
                    
                
            
        
    

还有这个:

import SwiftUI

struct FloorDetailedView: View 
    let ucmData = Bundle.main.decode(UcmData.self, from: "ucm_data.json")
    let buildingId: Int?
    let floorId: Int?
    let floorTitle: String?
    let buildingTitle: String?

    init(buildingId: Int? = nil, floorId: Int? = nil, floorTitle: String? = "nil", buildingTitle: String? = "nil") 
        self.buildingId = buildingId
        self.floorId = floorId
        self.floorTitle = floorTitle
        self.buildingTitle = buildingTitle

    

    var body: some View 
            List 
                ForEach(ucmData.building)  building in
                    if (building.id == self.buildingId) 
                        ForEach(building.floor)  floor in
                            if (floor.id == self.floorId) 
                                CabinetView(floor: floor, floorId: self.floorId)
                            
                        
                    
                
            
            .padding(.horizontal)
            .padding(.bottom)
            .navigationBarTitle(Text(self.buildingTitle! + " - " + self.floorTitle!), displayMode: .inline)
    

【讨论】:

以上是关于无法在合理的时间内对该表达式进行类型检查 - ForEach SwiftUI的主要内容,如果未能解决你的问题,请参考以下文章

编译器无法在合理的时间内对该表达式进行类型检查

编译器无法在 SwiftUI 中的合理时间内对该表达式进行类型检查?

Xcode 编译器错误:编译器无法在合理的时间内对该表达式进行类型检查(Xcode 12.0 SwiftUI)

Xcode 12.0 12A7209 SwiftUI 错误:编译器无法在合理的时间内对该表达式进行类型检查

无法编译 SwiftUI ForEach 表

如何使用 SwiftUI 交替行?