I have created two pickers, one Category picker, and one Item picker, in separate views. Based on excellent input in an earlier question, my Category picker now successfully filters Items in the Item picker and selects the first Item in the selected Category.On the surface, everything looks OK, but the count of items in the Item picker doesn't update after I select a new Category. It retains the count from the originally initialized Category even though the array it is based on now has more items in it.Image and Video of below app and issue can be found here:https://imgur.com/a/eqyrgbFIs there some syntax issue I am overlooking, or am I approaching it completely wrong?/*
Situation:
Two pickers, one selecting a category and the other selecting the items in that category.
Challenge:
The second picker shows the right items, but with the Count from the first category.
To see issue, copy below code into ContentView on a single-view app.
*/
import SwiftUI
struct Item: Identifiable {
var id = UUID()
var category:String
var item:String
}
let myCategories:[String] = ["Category 1","Category 2"]
let myItems:[Item] = [
Item(category: "Category 1", item: "Item 1.1"),
Item(category: "Category 1", item: "Item 1.2"),
Item(category: "Category 2", item: "Item 2.1"),
Item(category: "Category 2", item: "Item 2.2"),
Item(category: "Category 2", item: "Item 2.3"),
Item(category: "Category 2", item: "Item 2.4"),
]
class MyObject: ObservableObject {
// Select Category variables
@Published var selectedCategory:String = myCategories[0]
@Published var selectedCategoryItems:[Item] = []
@Published var selectedCategoryInt:Int = 0 {
didSet {
selectCategoryActions(selectedCategoryInt)
}
}
// Select Item variables
@Published var selectedItem:Item = myItems[0]
@Published var selectedItemInt:Int = 0 {
didSet {
selectedItem = selectedCategoryItems[selectedItemInt]
}
}
// Initialize values
init() {
selectCategoryActions(selectedCategoryInt)
}
// Actions when selecting a new category
func selectCategoryActions(_ selectedCategoryInt:Int) {
selectedCategory = myCategories[selectedCategoryInt]
// Get items in category
selectedCategoryItems = myItems.filter{ $0.category.contains(selectedCategory)}
// Select item in category
let selectedItemIntWrapped:Int? = myItems.firstIndex { $0.category == selectedCategory }
if let selectedItemInt = selectedItemIntWrapped {
self.selectedItem = myItems[selectedItemInt]
}
}
}
// Picker for selecting a category
struct SelectCategory: View {
var myCategories:[String]
@Binding var selectedCategoryInt:Int
var body: some View {
VStack {
Picker(selection: self.$selectedCategoryInt, label: Text("Select category")) {
ForEach(0 ..< self.myCategories.count, id: \.self) {
Text("\(self.myCategories[$0])")
}
}.labelsHidden()
}
}
}
// Picker for selecting items within the category
struct SelectItem: View {
var selectedCategoryItems:[Item]
@Binding var selectedItemInt:Int
var body: some View {
VStack {
// MARK: Something is going wrong here. I don't get right Item ID's
Picker(selection: self.$selectedItemInt, label: Text("Select item")) {
ForEach(0 ..< self.selectedCategoryItems.count, id: \.self) {
Text("\(self.selectedCategoryItems[$0].item)")
}
/*ForEach(selectedCategoryItems, id: \.id) { item in
Text("\(item.content)")
}*/
}.labelsHidden()
}
}
}
struct ContentView: View {
@ObservedObject var myObject = MyObject()
var body: some View {
VStack(alignment: .center, spacing: 10) {
Text("When selecting Category 2, only two of the four items are shown.")
Text("Selected category: \(myObject.selectedCategory)")
Text("Items in category: \(myObject.selectedCategoryItems.count)")
Text("Selected item: \(myObject.selectedItem.item)")
SelectCategory(
myCategories: myCategories,
selectedCategoryInt: self.$myObject.selectedCategoryInt)
SelectItem(
selectedCategoryItems: self.myObject.selectedCategoryItems,
selectedItemInt: self.$myObject.selectedItemInt)
Spacer()
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Post
Replies
Boosts
Views
Activity
My goal is to have multiple views with a two-way binding to the same variable. I have created a view with two embedded views that all update the same ObservableObject. However, when one view updates it, only that view can see the change. None of the others get updated. The code compiles just fine, but the views act as if they each have a local variable instead of sharing a published one.What have I missed in how to make this work?import SwiftUI
class UserSettings: ObservableObject {
@Published var score:Int = 0
}
struct ButtonOne: View {
@ObservedObject var settings = UserSettings()
var body: some View {
HStack {
Button(action: {
self.settings.score += 1
}) {
Text("Increase Score")
}
Text("In ButtonOne your score is \(settings.score)")
}
}
}
struct ButtonTwo: View {
@ObservedObject var settings = UserSettings()
var body: some View {
HStack {
Button(action: {
self.settings.score -= 1
}) {
Text("Decrease Score")
}
Text("In ButtonTwo your score is \(settings.score)")
}
}
}
struct ContentView: View {
@ObservedObject var settings = UserSettings()
var body: some View {
VStack(spacing: 10) {
Text("In master view your score is \(settings.score)")
ButtonOne()
ButtonTwo()
Text("All scores refer to the same variable, so should be the same.")
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
I have a Category Picker and an Item Picker. When picking a category, it filters the items you can see in the Item picker. Based on excellent input earlier in this Forum, I've managed to get it to work with one small caveat: The count of items doesn't change when you change category.Have I made a syntax error somewhere, or is my approach completely wrong?import SwiftUI
// Data
struct Item: Identifiable {
var id = UUID()
var category:String
var item:String
}
let myCategories:[String] = ["Category 1","Category 2"]
let myItems:[Item] = [
Item(category: "Category 1", item: "Item 1.1"),
Item(category: "Category 1", item: "Item 1.2"),
Item(category: "Category 2", item: "Item 2.1"),
Item(category: "Category 2", item: "Item 2.2"),
Item(category: "Category 2", item: "Item 2.3"),
Item(category: "Category 2", item: "Item 2.4"),
]
// Factory
class MyObject: ObservableObject {
// Category picker variables
@Published var selectedCategory:String = myCategories[0]
@Published var selectedCategoryItems:[Item] = []
@Published var selectedCategoryInt:Int = 0 {
didSet {
selectCategoryActions(selectedCategoryInt)
}
}
// Item picker variables
@Published var selectedItem:Item = myItems[0]
@Published var selectedItemInt:Int = 0 {
didSet {
selectedItem = selectedCategoryItems[selectedItemInt]
}
}
// Initial category selection
init() {
selectCategoryActions(selectedCategoryInt)
}
// Actions when selecting a new category
func selectCategoryActions(_ selectedCategoryInt:Int) {
selectedCategory = myCategories[selectedCategoryInt]
// Get items in category
selectedCategoryItems = myItems.filter{ $0.category.contains(selectedCategory)}
// Select initial item in category
let selectedItemIntWrapped:Int? = myItems.firstIndex { $0.category == selectedCategory }
if let selectedItemInt = selectedItemIntWrapped {
self.selectedItem = myItems[selectedItemInt]
}
}
}
// View
struct ContentView: View {
@ObservedObject var myObject = MyObject()
var body: some View {
VStack(spacing: 10) {
Section(header: Text("Observable Object")) {
Text("Category 2 has four items, but only the first two are shown.")
Text("Selected category: \(myObject.selectedCategory)")
Text("Items in category: \(myObject.selectedCategoryItems.count)")
Text("Selected item: \(myObject.selectedItem.item)")
Picker(selection: self.$myObject.selectedCategoryInt, label: Text("Select category")) {
ForEach(0 ..< myCategories.count, id: \.self) {
Text("\(myCategories[$0])")
}
}.labelsHidden()
// MARK: Something is going wrong here. The count doesn't update after picking a new category.
Picker(selection: self.$myObject.selectedItemInt, label: Text("Select object item")) {
ForEach(0 ..< self.myObject.selectedCategoryItems.count, id: \.self) {
Text("\(self.myObject.selectedCategoryItems[$0].item)")
}
/*ForEach(selectedCategoryItems, id: \.id) { item in
Text("\(item.content)")
}*/
}.labelsHidden()
}
Spacer()
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}