在 ForEach 内部循环时 DisclosureGroup 未打开

DisclosureGroup not opening when looped inside ForEach

我正在尝试添加一个支持部分(这是一个变成更多内容的演示)并认为我可以获取一个 json 文件并将其添加到最终用户的 DisclosureGroup 中。

本来以为是网络问题,结果在本地添加文件还是一样的问题

当我在模拟器中 运行 尝试打开其中一个 DisclosureGroup 项目时,它打不开。如果我按下更多,RAM 使用量会增加,但看不出为什么它应该在初始 Bundle 加载到阵列之后。

这是我测试的数据:

SupportQuestions.json

{
 "sections": [
  {
   "title": "Section title 1",
   "description": null,
   "questions": [
    {
     "title": "Question title 1",
     "response": "Answer 1"
    },
    {
     "title": "Question title 3",
     "response": "Answer 3"
    }
   ]
  },
  {
   "title": "Section title 2",
   "description": "Section description",
   "questions": [
    {
     "title": "Question title 4",
     "response": "Answer 4"
    },
    {
     "title": "Question title 5",
     "response": "Answer 5"
    },
    {
     "title": "Question title 6",
     "response": "Answer 6"
    }
   ]
  },
  {
   "title": "Section title 3",
   "description": "Another section description",
   "questions": [
    {
     "title": "Question title 7",
     "response": "Answer 7"
    },
    {
     "title": "Question title 8",
     "response": "Answer 8"
    },
    {
     "title": "Question title 9",
     "response": "Answer 9"
    }
   ]
  }
 ]
}

然后我在视图中使用的 Swift:

struct SettingsHelpView: View {

  @State private
  var suppportItems: [SupportSections.SupportCategory] = []
  var body: some View {
    Form {
      ForEach(suppportItems) {
        item in
          Section {
            ForEach(item.questions) {
              question in
                DisclosureGroup {
                  Text(question.response)
                }
              label: {
                Text(question.title).bold()
              }
            }
          }
        header: {
          Text(item.title)
        }
        footer: {
          Text(item.decription ?? "")
        }
      }
    }
    .onAppear {
      fetchHelpSection()
    }
  }

  private func fetchHelpSection() {
    let questions = Bundle.main.decode(SupportSections.self, from: "SupportQuestions.json")
    suppportItems = questions.sections
  }
}

型号

struct SupportSections: Decodable {
 let sections: [SupportCategory]

 struct SupportCategory: Decodable, Identifiable {
  var id: String { UUID().uuidString }
  let title: String
  let decription: String?
  let questions: [SupportQuestion]

  struct SupportQuestion: Decodable, Identifiable {
   var id: String { UUID().uuidString }
   let title: String
   let response: String
  }
 }
}

捆绑+扩展

extension Bundle {
 func decode<T: Decodable>(_ type: T.Type, from file: String, dateDecodingStategy: JSONDecoder.DateDecodingStrategy = .deferredToDate, keyDecodingStrategy: JSONDecoder.KeyDecodingStrategy = .useDefaultKeys) -> T {

  guard let url = self.url(forResource: file, withExtension: nil) else {
   fatalError("Error: Failed to locate \(file) in bundle.")
  }
  guard let data = try? Data(contentsOf: url) else {
   fatalError("Error: Failed to load \(file) from bundle.")
  }
  let decoder = JSONDecoder()
  decoder.dateDecodingStrategy = dateDecodingStategy
  decoder.keyDecodingStrategy = keyDecodingStrategy
  guard let loaded = try? decoder.decode(T.self, from: data) else {
   fatalError("Error: Failed to decode \(file) from bundle.")
  }
  return loaded
 }
}

正在发生的事情的视频(抱歉不知道如何调整大小):

问题出在模型中的 id 属性。现在,您将 id 定义为计算的 属性:

var id: String { UUID().uuidString }

这意味着每次 SwiftUI 请求 id 时,它都会得到一个 不同的 值,因为每次都会生成一个新的 UUID。这让 SwiftUI 感到困惑,它 'closes' 和 DisclosureGroup 因为它认为它是一个新的 View(因为新 ID)。

要解决此问题,请将您的 id 属性声明为 non-computed 值并提供 CodingKeys 以便系统不会尝试从 JSON 文件解码 属性。

struct SupportSections: Decodable {
    let sections: [SupportCategory]
    
    struct SupportCategory: Decodable, Identifiable {
        var id = UUID().uuidString //<-- Here
        let title: String
        let description: String? //note that you had a typo here in your original code
        let questions: [SupportQuestion]
        
        enum CodingKeys : String, CodingKey {
            case title, description, questions
        }
        
        struct SupportQuestion: Decodable, Identifiable {
            var id: String = UUID().uuidString //<-- Here
            let title: String
            let response: String
            
            enum CodingKeys : String, CodingKey {
                case title, response
            }
        }
    }
}