SwiftUI iterating through dictionary with ForEach
Simple answer: no.
As you correctly pointed out, a dictionary is unordered. The ForEach watches its collection for changes. These changes includes inserts, deletions, moves and update. If any of those changes occurs, an update will be triggered. Reference: https://developer.apple.com/videos/play/wwdc2019/204/ at 46:10:
A ForEach automatically watches for changes in his collection
I recommend you watch the talk :)
You can not use a ForEach because:
- It watches a collection and monitors movements. Impossible with an unorered dictionary.
- When reusing views (like a
UITableView
reuses cells when cells can be recycled, aList
is backed byUITableViewCells
, and I think aForEach
is doing the same thing), it needs to compute what cell to show. It does that by querying an index path from the data source. Logically speaking, an index path is useless if the data source is unordered.
Loop through Dictionary in SwiftUI
I recommend to map the (keys of the) dictionary to an array of a region struct
struct Region {
let name : String
let countries : [Country]
}
This can be used in a ForEach
statement with id: \.name
. Further consider that a dictionary is unordered.
This is a simple example with a string array representing the countries
let dictionary = ["Asia": ["Japan", "India"], "Europe": ["Italy", "Norway"]]
struct Region {
let name : String
let countries : [String]
}
let regions = dictionary
.keys
.sorted()
.map{Region(name: $0, countries: dictionary[$0]!)}
NavigationView {
List {
ForEach(regions, id: \.name) { region in
}
}
}
How use use SwiftUI ForEach with a dictionary [ customEnum : customStrut ] - getting conform to 'RandomAccessCollection' error
As states dictionary is not "random access" capable collection, so it cannot be used directly in ForEach, here is possible approach
HStack {
ForEach(Array(filterSelections.keys.enumerated()), id:\.element) { _, key in
Text("TBD \(self.filterSelections[key]?.title ?? "")")
// Will be putting checkboxes here - i.e. so can chose which ones
}
}
SwiftUI ForEach of dictionary identified issue
It looks like you have a misunderstanding. Based on the code you posted, I guess you think that iterating over a dictionary iterates over the values in the dictionary. But that is not how dictionary iteration works.
When you iterate over a dictionary, you receive pairs. Each pair contains one key and the corresponding value from the dictionary. In your code, wordListEntry
's type is (key: Int, value: [String: String])
, a pair whose first element is key
of type Int
and whose second element is value
of type [String: String]
.
I think you want to just iterate over the dictionary's keys, and then look up the corresponding values inside the ForEach
body, like this:
ForEach(wordList.keys.sorted().identified(by: \.self)) { key in
let lang1 = wordListEntry[wordOrder[0]]
let lang2 = wordListEntry[wordOrder[1]]
return WordRow(lang1, lang2)
}
Is it possible to iterate a dictionary inside ForEach in SwiftUI?
This is because a Dictionary is unordered. So its keys are unordered as well.
You need to make it ordered using Array
:
ForEach(Array(viewModel.userDomains.keys).indices, id: \.self) { ...
As they are ordered randomly, you may also want to sort them:
ForEach(Array(viewModel.userDomains.keys).sorted().indices, id: \.self) { ...
But as the ordering and sorting gets more complex, you may think of moving the logic to the ViewModel
instead.
SwiftUI How to make ForEach with Dict with array of custom objects auto update
try something like this:
List {
ForEach(Array(coupons.keys), id: \.self) { key in
// or ForEach(coupons.keys.sorted(), id: \.self) { key in
Section(header: Text(key)) {
ForEach(coupons[key] ?? [], id: \.id) { coupon in
Button(ifexpiredString(date: coupon.date)+coupon.data[1]) {
let coupons = UserDefaults.standard.coupons
let couponIndex = coupons.firstIndex(of: coupon)
self.currentview = "Coupon"+String(couponIndex!)
}.foregroundColor(ifexpired(date: coupon.date) ? Color.gray : ifexpireing(date: coupon.date))
}
}
}
}
Iterating through set with ForEach in SwiftUI
It turns out need some playing with code like this way, if you know better way I will accept your answer. thanks.
PS: For using Set in ForEach and unleashing full power of Set, we can use an identifiable type for elements for our Set, then using id: \.id
, after that we can have multiple elements with same string and deferent id's also the power of Set.
struct ContentView: View {
@State private var set: Set<String> = ["value1", "value2", "value3", "value4"]
var body: some View {
SetforForEachWithBinding(set: $set)
}
}
struct SetforForEachWithBinding: View {
@Binding var set: Set<String>
var body: some View {
List {
ForEach(set.sorted(by: <), id: \.self) { element in
CustomTextFieldView(string: Binding(
get: { return element },
set: { newValue in
set.remove(element)
set.insert(newValue)
}
))
}
}
}
}
struct CustomTextFieldView: View {
@Binding var string: String
@State private var isUnderEdit: Bool = Bool()
@State private var isUnderEditString: String = String()
var body: some View {
TextField("Enter value here...", text: Binding(
get: {
if isUnderEdit {
return isUnderEditString
}
else {
return string
}
},
set: { newValue in
if isUnderEdit {
isUnderEditString = newValue
}
}
), onEditingChanged: { value in
if value {
isUnderEdit = true
isUnderEditString = string
}
else {
isUnderEdit = false
string = isUnderEditString
}
}, onCommit: { })
}
}
Related Topics
Cannot Assign Property in Method of Struct
How to Encode a String to Base64 in Swift
Can Swift Convert a Class/Struct Data into Dictionary
How to Create Generic Protocols in Swift
Extend Array Types Using Where Clause in Swift
Unwrapping Multiple Optionals in If Statement
Convert a Two Byte Uint8 Array to a Uint16 in Swift
How to Detect a Tap Gesture Location in Swiftui
Calculate Age from Birth Date Using Nsdatecomponents in Swift
Swift - Iboutletcollection Equivalent
What Does the Dollar Sign Do in Swift/Swiftui
Can Swift Return Value from an Async Void-Returning Block
How to Detect Which Skspritenode Has Been Touched
Iterate Over Collection Two At a Time in Swift
First Item in a List Is Always Selected
Xcode 11 & Ios13, Using Uikit Can't Change Background Colour of Uiviewcontroller