SwiftUI:如何将嵌套JSON数据自动加载到自定义树形视图
我正在开发一款树形结构的应用,需要将嵌套JSON文件中的数据加载到自定义树形结构中。目前已掌握静态构建树形结构的方法,但希望实现数据获取完成后自动生成树形结构。以下是相关代码实现:
自定义树形结构
struct Tree<A> { var value: A var children: [Tree<A>] = [] init(_ value: A, children: [Tree<A>] = []) { self.value = value self.children = children } } extension Tree { func map<B>(_ transform: (A) -> B) -> Tree<B> { return Tree<B>(transform(value), children: children.map({ $0.map(transform) })) } } class Unique<A>: Identifiable { let value: A init(_ value: A) { self.value = value } } struct CollectDict<Key: Hashable, Value>: PreferenceKey { static var defaultValue: [Key:Value] { [:] } static func reduce(value: inout [Key:Value], nextValue: () -> [Key:Value]) { value.merge(nextValue(), uniquingKeysWith: { $1 }) } } extension CGPoint: VectorArithmetic { public static func -= (lhs: inout CGPoint, rhs: CGPoint) { lhs = lhs - rhs } public static func - (lhs: CGPoint, rhs: CGPoint) -> CGPoint { return CGPoint(x: lhs.x - rhs.x, y: lhs.y - rhs.y) } public static func += (lhs: inout CGPoint, rhs: CGPoint) { lhs = lhs + rhs } public mutating func scale(by rhs: Double) { x *= CGFloat(rhs) y *= CGFloat(rhs) } public static func + (lhs: CGPoint, rhs: CGPoint) -> CGPoint { return CGPoint(x: lhs.x + rhs.x, y: lhs.y + rhs.y) } public var magnitudeSquared: Double { return Double(x*x + y*y) } } struct Line: Shape { var from: CGPoint var to: CGPoint var animatableData: AnimatablePair<CGPoint, CGPoint> { get { AnimatablePair(from, to) } set { from = newValue.first to = newValue.second } } func path(in rect: CGRect) -> Path { Path { p in p.move(to: self.from) p.addLine(to: self.to) } } } struct Diagram<A: Identifiable, V: View>: View { let tree: Tree<A> let node: (A) -> V typealias Key = CollectDict<A.ID, Anchor<CGPoint>> var body: some View { VStack(alignment: .center) { node(tree.value) .anchorPreference(key: Key.self, value: .center, transform: { [self.tree.value.id: $0] }) HStack(alignment: .bottom, spacing: 10) { ForEach(tree.children, id: \.value.id, content: { child in Diagram(tree: child, node: self.node) }) } }.backgroundPreferenceValue(Key.self, { (centers: [A.ID: Anchor<CGPoint>]) in GeometryReader { proxy in ForEach(self.tree.children, id: \.value.id, content: { child in Line( from: proxy[centers[self.tree.value.id]!], to: proxy[centers[child.value.id]!]) .stroke() }) } }) } }
树形视图
struct RoundedCircleStyle: ViewModifier { func body(content: Content) -> some View { content .frame(width: 50, height: 50) .background(Circle().stroke()) .background(Circle().fill(Color.white)) .padding(10) } } struct BinaryTreeView: View { @State var tree = Tree<Friut>(Friut(header: "", info: "", children: [Friut]()), children: [Tree<Friut>]()).map(Unique.init) @State var friut = Friut(header: "", info: "", children: [Friut]()) @State var pickedFriutID = "" var body: some View { VStack { Diagram(tree: tree, node: { value in Text("\(value.value.header)") .modifier(RoundedCircleStyle()) }) }.onAppear { loadJson(filename: "test") let binaryTree = Tree<Friut>(friut, children: [ Tree(friut.children.first!), Tree(friut.children.last!) ]) tree = binaryTree.map(Unique.init) } } func loadJson(filename fileName: String) { if let url = Bundle.main.url(forResource: fileName, withExtension: "json") { do { let data = try Data(contentsOf: url) let decoder = JSONDecoder() let jsonData = try decoder.decode(JSONData.self, from: data) friut = jsonData.Fruits pickedFriutID = friut.id } catch { print("error:\(error)") } } } }
Fruit结构体
struct JSONData: Decodable { let Fruits: Friut } struct Friut: Decodable, Identifiable { let id: String = UUID().uuidString var header: String var info: String var children: [Self] }
JSON文件
{ "Fruits": { "header": "Apple", "info": "Apples are common fruits in Europe", "children": [ { "header": "Golden Delicous", "info": "The Golden Delicous is one of the most popular Apples in the United States", "children" : [ { "header": "Golden Delicous Lidl", "info": "The Golden Delicous sold from Lidl is more sour than the average of its kind", "children" : []}, { "header": "Golden Delicous Aldi", "info": "The Golden Delicous Aldi is a steal for only 0.20ct per Apple!", "children" : [] }] }, { "header": "Pink Lady", "info": "The Pink Lady is very sweet apple", "children":[ { "header": "Pink Lady America", "info": "The American Pink Lady is much more red but less aromatic than other Pink Lady Apples", "children": [ { "header": "Pink Lady America 1", "info": "Digusting 1", "children" : [ { "header": "Pink Lady America 1.1", "info": "Disgusting 1.1", "children" : [] }, { "header": "Pink Lady America 1.2", "info": "Disgusting 1.2", "children": [ { "header" : "Pink Lady America 1.2.1", "info": "Disgusting 1.2.1", "children": [ { "header": "Pink Lady America 1.2.1.1", "info":"Disgusting 1.2.1.1", "children": []}, { "header": "Pink Lady America 1.2.1.2", "info":"Disgusting 1.2.1.2", "children": [] }] }] }] }, { "header": "Pink Lady America 2", "info": "Digusting 2", "children" :[] }] }, { "header": "Pink Lady Europe", "info" : "The European Pink Lady is much more aromatic than the American one but does not look as appetizing", "children" : [] }] } ] } }
内容的提问来源于stack exchange,提问作者green8
相关产品推荐
相关产品推荐

