使用来自另一个结构的指定变量初始化SwiftUI结构

时间:2020-06-29 08:32:14

标签: swift swiftui

我有一个数组buildingData,其中包含具有数字属性(当前存储为字符串)的Building结构。我正在制作一个视图,该视图提供单个建筑物的详细信息,并显示其每个属性相对于buildingData中其他建筑物的排名编号。

与其更明确地编写列表的每一行,我不希望使用通用视图结构rankRow,如下所示:

    struct rankingRow : View {
        var building : Building
        var dataDescription : String
        var dataValue: String
        
        var body: some View {
            HStack {
                Text(dataDescription)
                    .fontWeight(.bold)
                Text(dataValue)
                Spacer()
                RankingNumber(      //  Takes a rank (Int) and a size (Double) and makes a ranking graphic
                    rank: buildingData.sorted(by: { Double($0.overallSqm) ?? 0 > Double($1.overallSqm) ?? 0 }).firstIndex(of: building)! + 1,
                    size:28
                    )
            }
        }
    }

但是,我不清楚如何简洁地将此结构传递给要排名的属性的名称(例如,上例中的totalSqm)。如何更改结构以使用以下方式生成列表:

           List {
                             rankingRow(building:building, dataDescription:"Total Space (m2):", dataValue: building.overallSqm, rankedBy: .overallSqm)
                             rankingRow(building:building, dataDescription:”Public Space (m2):", dataValue: building.publicSqm, rankedBy: .publicSqm)
            }

1 个答案:

答案 0 :(得分:0)

现在,您的RankRow(s)取决于buildingData。如果您添加排名(以及其他 图形可能需要的数据)作为参数,可以简化视图。计算 排名最好在模型(或ViewModel)中进行(或扩展)。

要遍历建筑物的属性,可以在建筑物上实现下标。 使用枚举可避免需要检查无效键。您可以将扩展名添加到 方便提供数据描述的关键。使用ForEach遍历 在SwiftUI中构建属性。

import SwiftUI

struct Building {
    let overallSqm: String
    let publicSqm : String
}

// identifying buildings is necessary for calculating the rank, one of the options
// is to make Buidling conform to equatable
extension Building: Equatable {
    static func ==(lhs: Building, rhs: Building) -> Bool {
        return lhs.overallSqm == rhs.overallSqm && lhs.publicSqm == rhs.publicSqm
    }
}

extension Building {

    enum PropertyKey {
        case overallSqm
        case publicSqm
    }

    subscript(key: PropertyKey) -> String {
        switch key {
            case .overallSqm: return self.overallSqm
            case .publicSqm: return self.publicSqm
        }
    }
}

extension Building {
    func rank(in buildings: [Building], key: Building.PropertyKey) -> Int {
        (buildings.sorted { (Double($0[key]) ?? 0) < (Double($1[key]) ?? 0) }.firstIndex { self == $0 } ?? 0) + 1
    }
}

extension Building.PropertyKey {
    var dataDescription: String {
        switch self {
        case .overallSqm: return "Total Space (m2):"
        case .publicSqm: return "Public Space (m2):"
        }
    }
}

struct rankingRow: View {

    let building       : Building
    let dataDescription: String
    let dataValue      : String
    let rank           : Int
    
    var body: some View {
        HStack {
            Text(dataDescription)
                .fontWeight(.bold)
            Text(dataValue)
            Spacer()
            RankingNumber(rank: rank, size: 28)
        }
    }
}

struct RankingNumber: View {
    let rank: Int
    let size: CGFloat
    
    var body: some View {
        Text("Rank: \(rank)")
    }
}

struct ContentView: View {

    let buildings: [Building] = [
        Building(overallSqm: "1000.0", publicSqm: "700.0" ),
        Building(overallSqm: "1500.0", publicSqm: "600.0" ),
        Building(overallSqm: "2000.0", publicSqm: "1200.0")
    ]
    
    @State var building: Building = Building(overallSqm: "1000.0", publicSqm: "700.0")

    let keys: [Building.PropertyKey] = [.overallSqm, .publicSqm]
            
    func rank(for building: Building, key: Building.PropertyKey) -> Int {
        (buildings.sorted { (Double($0[key]) ?? 0) < (Double($1[key]) ?? 0) }.firstIndex { building == $0 } ?? 0) + 1
    }
    
    var body: some View {
        List {
            ForEach(keys, id: \.self) { key in
                rankingRow(
                    building       : self.building,
                    dataDescription: key.dataDescription,
                    dataValue      : self.building[key],
                    rank           : self.rank(for: self.building, key: key))
            }
        }
    }
}