SwiftUI iterating through dictionary with ForEach

后端 未结 7 1845

Is there a way to iterate through a Dictionary in a ForEach loop? Xcode says

Generic struct \'ForEach\' requires that \'[Str

相关标签:
7条回答
  • 2020-12-03 13:53

    Here is how I implemented this:

    struct CartView: View {
        var cart:[String: Int]
    
        var body: some View {
            List {
                ForEach(cart.keys.sorted()) { key in
                    Text(key)
                    Text("\(cart[key]!)")
                }
            }
        }
    }
    

    The first Text View will output the key which is a String. The second Text View will output the value of the Dict at that key which is an Int. The ! that follows this is to unwrap the Optional which contains this Int. In production you would perform checks on this Optional for a more safe way of unwrapping it, but this is a proof of concept.

    0 讨论(0)
  • 2020-12-03 13:56

    Xcode: 11.4.1~

        ...
        var testDict: [String: Double] = ["USD:": 10.0, "EUR:": 10.0, "ILS:": 10.0]
        ...
        ForEach(testDict.keys.sorted(), id: \.self) { key in
            HStack {
                Text(key)
                Text("\(testDict[key] ?? 1.0)")
            }
        }
        ...
    

    more detail:

    final class ViewModel: ObservableObject {
        @Published
        var abstractCurrencyCount: Double = 10
        @Published
        var abstractCurrencytIndex: [String: Double] = ["USD:": 10.0, "EUR:": 15.0, "ILS:": 5.0]
    }
    
    struct SomeView: View {
        @ObservedObject var vm = ViewModel()
        var body: some View {
            NavigationView {
                List {
                    ForEach(vm.abstractCurrencytIndex.keys.sorted(), id: \.self) { key in
                        HStack {
                            Text(String(format: "%.2f", self.vm.abstractCurrencyCount))
                            Text("Abstract currency in \(key)")
                            Spacer()
                            Text(NumberFormatter.localizedString(from: NSNumber(value: self.vm.abstractCurrencytIndex[key] ?? 0.0), number: .currency))
                        }
                    }
                }
            }
        }
    }
    
    0 讨论(0)
  • 2020-12-03 13:58

    Since it's unordered, the only way is to put it into an array, which is pretty simple. But the order of the array will vary.

    struct Test : View {
    let dict: [String: Int] = ["test1": 1, "test2": 2, "test3": 3]
    var body: some View {
        let keys = dict.map{$0.key}
        let values = dict.map {$0.value}
    
        return List {
            ForEach(keys.indices) {index in
                HStack {
                    Text(keys[index])
                    Text("\(values[index])")
                }
            }
        }
    }
    }
    
    #if DEBUG
    struct Test_Previews : PreviewProvider {
        static var previews: some View {
            Test()
        }
    }
    #endif
    
    0 讨论(0)
  • 2020-12-03 14:05

    The syntax errors I encountered using code from some of the answers to this post helped me sort out a solution for my own problem...

    Using a dictionary that contained:

    • key = a CoreDataEntity;
    • value = the count of instances of that entity from a relationship (type NSSet).

    I highlight the answer by @J.Doe and comments that an unordered / random collection Dictionary may not be the best solution to use with table view cells (AppKit NSTableView / UIKit UITableView / SwiftUI List rows).

    Subsequently, I'll be rebuilding my code to instead work with arrays.

    But if you must work with a Dictionary, here is my working solution:

    struct MyView: View {
        
        var body: some View {
            
            // ...code to prepare data for dictionary...
    
            var dictionary: [CoreDataEntity : Int] = [:]
    
            // ...code to create dictionary...
    
            let dictionarySorted = dictionary.sorted(by: { $0.key.name < $1.key.name })
            // where .name is the attribute of the CoreDataEntity to sort by
            
            VStack {  // or other suitable view builder/s
                ForEach(dictionarySorted, id: \.key) { (key, value) in
                    Text("\(value) \(key.nameSafelyUnwrapped)")
                }
            }
        }
    }
    
    extension CoreDataEntity {
        var nameSafelyUnwrapped: String {
            guard let name = self.name else {
                return String() // or another default of your choosing
            }
            return name
        }
    }
    
    0 讨论(0)
  • 2020-12-03 14:06

    You can sort your dictionary to get (key, value) tuple array and then use it.

    struct ContentView: View {
        let dict = ["key1": "value1", "key2": "value2"]
        
        var body: some View {
            List {
                ForEach(dict.sorted(by: >), id: \.key) { key, value in
                    Section(header: Text(key)) {
                        Text(value)
                    }
                }
            }
        }
    }
    
    0 讨论(0)
  • 2020-12-03 14:09

    I was trying to figure this out as well with a Dictionary of enum/Int pairs. I am effectively converting it to array as Andre suggests, but instead of using map I just cast the keys.

    enum Fruits : Int, CaseIterable {
        case Apple = 0
        case Banana = 1
        case Strawberry = 2
        case Blueberry = 3
    }
    
    struct ForEachTest: View {
        var FruitBasket : [Fruits: Int] = [Fruits.Apple: 5, Fruits.Banana : 8, Fruits.Blueberry : 20]
        var body: some View {
            VStack {
                ForEach([Fruits](FruitBasket.keys), id:\Fruits.hashValue) { f in
                    Text(String(describing: f) + ": \(self.FruitBasket[f]!)")
                }
            }
        }
    }
    
    struct ForEachTest_Previews: PreviewProvider {
        static var previews: some View {
            ForEachTest()
        }
    }
    
    0 讨论(0)
提交回复
热议问题