问题
How could I correct this code from failing build? Basically wanting to use ForEach to iterate through a dictionary which is based on a [ customEnum : customStrut ].
Otherwise if this is problematic a different way to achieve that SwiftUI supports?
Errors
Referencing initializer 'init(_:id:content:)' on 'ForEach' requires that '[GCFilterViewOptions.FilterOptions : GCFilterViewOptions.FilterOptionValues]' conform to 'RandomAccessCollection'
Type '(key: GCFilterViewOptions.FilterOptions, value: GCFilterViewOptions.FilterOptionValues)' cannot conform to 'Hashable'; only struct/enum/class types can conform to protocols
Code
import SwiftUI
struct GCFilterViewOptions: View {
enum FilterOptions {
case NewLine
case Comma
case Space
}
struct FilterOptionValues {
var title : String
var selected : Bool
}
var filterSelections : [FilterOptions : FilterOptionValues] = [
FilterOptions.NewLine : FilterOptionValues(title: "New Line", selected: true),
FilterOptions.Comma : FilterOptionValues(title: "Comma", selected: true),
FilterOptions.Space : FilterOptionValues(title: "Space", selected: false)
]
var body : some View {
HStack {
ForEach(filterSelections, id:\.self) { filterOption in. // ** ERRORS HERE **
Text("TBD")
// Will be putting checkboxes here - i.e. so can chose which ones
}
}
}
}
回答1:
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
}
}
回答2:
ForEach loops only support RandomAccess capable data structures (Apple's documentation about what is RandomAccessCollection great).
If you do not really need the dictionary structure you can use a simple struct. This is basically the same as yours but the enum is nested into it and is a property:
struct FilterOption {
enum FilterOptionType {
case newLine
case comma
case space
}
var type: FilterOptionType
var title: String
var selected: Bool
}
var filterSelection: [FilterOption] = [
FilterOption(type: .newLine, title: "New line", selected: true),
FilterOption(type: .comma, title: "Comma", selected: false),
FilterOption(type: .space, title: "Space", selected: true),
]
If you want yo used a dictionary (for example to assert that options are only listed once) you should keep your current data structure and just use the dictionary keys to access the elements. Just keep in mind that a dictionary is not ordered, if you want a stable order you should sort it (for example with the rawValue of the keys):
enum FilterOption: String {
case newLine
case comma
case space
}
struct FilterOptionValue {
var title: String
var selected: Bool
}
var filterSelection: [FilterOption: FilterOptionValue] = [
.newLine: FilterOptionValue(title: "New Line", selected: true),
.comma: FilterOptionValue(title: "Comma", selected: true),
.space: FilterOptionValue(title: "Space", selected: false)
]
// Guarantees a stable print order
for option in filterSelection.keys.sorted(by: { $0.rawValue < $1.rawValue }) {
let optionValue = filterSelection[key]!
print(option, optionValue)
}
Note that in Swift enum cases are lowercase as properties are. Only types begin with a capital letter (newLine
, not NewLine
).
Moreover enum type name should be singular as when instantiated they represent only a single option. Use FilterOption
instead of FilterOptions
.
Same for filterSelections
that should be singular as a selection already contains multiple elements.
来源:https://stackoverflow.com/questions/61440910/how-use-use-swiftui-foreach-with-a-dictionary-customenum-customstrut-get