Reputation: 430
I´m making a pretty simple use of the swiftUI List and EditMode in my project. It´s something like:
@State var selection = Set<UUID>()
List(selection: $selection) {
ForEach(items) { item in
Text(item.title)
}
}
So, when EditMode is activated and items in the list get pressed selection Set gets updated. What I would like is to observe these changes, more precisely, the size of the Set. For this I added property observers as such:
willSet {
let newCount = newValue.count
updateButtonText(count: newCount)
}
didSet {
let newCount = selection.count
updateButtonText(count: newCount)
}
But this only gets called when I reset the selection property. But not when items get selected. So I will assume appending items to a Set doesn't trigger the property observers, or perhaps its something more intrinsic as the selection parameter is handled behind the scenes by List.
The question however, remains the same, how can I keep a live counter of the selected items through EditMode? (I wouldn't want to build my own Edit Mode)
Upvotes: 0
Views: 367
Reputation: 52555
The most straightforward approach is probably using onChange
:
struct ContentView : View {
@State var selection = Set<UUID>()
@State var items : [Item] = [.init(title: "Test1"),
.init(title: "Test2"),
.init(title: "Test3")]
var body: some View {
NavigationView {
List(selection: $selection) {
ForEach(items) { item in
Text(item.title)
}
}
.navigationBarItems(trailing: EditButton())
.onChange(of: selection) { newSelection in
print(newSelection.count)
}
}
}
}
That being said, if all you're doing is updating a label, you don't even need that -- the view will be re-rendered when @State var selection
changes, so you can just do:
struct ContentView : View {
@State var selection = Set<UUID>()
@State var items : [Item] = [.init(title: "Test1"),
.init(title: "Test2"),
.init(title: "Test3")]
var body: some View {
NavigationView {
VStack {
Text("Number: \(selection.count)")
List(selection: $selection) {
ForEach(items) { item in
Text(item.title)
}
}
}
.navigationBarItems(trailing: EditButton())
}
}
}
Finally, just for completion, another approach would be to use a view model and Combine:
import SwiftUI
import Combine
class ViewModel : ObservableObject {
@Published var selection = Set<UUID>()
@Published var items : [Item] = [.init(title: "Test1"),
.init(title: "Test2"),
.init(title: "Test3")]
var cancellable : AnyCancellable?
init() {
cancellable = $selection.sink {
print($0.count)
}
}
}
struct ContentView : View {
@StateObject private var viewModel = ViewModel()
var body: some View {
NavigationView {
List(selection: $viewModel.selection) {
ForEach(viewModel.items) { item in
Text(item.title)
}
}
.navigationBarItems(trailing: EditButton())
}
}
}
Upvotes: 1