We loop through categories
, to create sections
. Later on we loop through indices
to actually display the content
from Message
struct
.
import SwiftUI
struct MultipleSectionsView: View {
@State private var messages = [
Message(content: "Hello", category: .important, isSelected: false),
Message(content: "World", category: .significant, isSelected: false),
Message(content: "Hello", category: .important, isSelected: false),
Message(content: "World", category: .significant, isSelected: false),
Message(content: "Hello", category: .important, isSelected: false),
Message(content: "World", category: .pointless, isSelected: false)
]
var body: some View {
Form {
ForEach(Message.MessageCategory.allCases, id: \.self) { category in
Section(category.rawValue.uppercased()) {
ForEach(messages.indices, id:\.self) { index in
if messages[index].category == category {
HStack {
Image(
systemName:
messages[index].isSelected ? "checkmark.circle" : "circle"
)
Text(messages[index].content)
}
.onTapGesture {
messages[index].toggleSelection()
}
}
}
}
}
}
}
}
struct Message: Identifiable {
let id = UUID()
let content: String
let category: MessageCategory
var isSelected: Bool
mutating func toggleSelection() {
isSelected = !isSelected
}
enum MessageCategory: String, CaseIterable {
case pointless, important, significant
}
}
In ForEach
we need to use id
. By using id
we observe changes to the indices as well. Otherwise we could possibly get a crash by running out of bounds. You can test this out by simply adding messages = messages.dropLast()
to the .onTapGesture
closure and removing id:.self
in the ForEach
.
Not the most efficient solution, but I guess it does the job.
Homework
: refactor code to a more efficient approach. At the moment we are looping through whole messages
for each category. The solution should allow getting the data, without looping through messages
for each category. Any other ideas for improvement?