1

If the variable comes from an array then the label is not automatically refreshed. Is there a specific reason for that?

@State private var categories: [ItemCategory] = getCategories()
@State private var isOn = true
Button(action: {
    categories[1].chose = !categories[1].chose
}, label: {
    Text(categories[1].chose ? "Add" : "Remove") // not automatically refreshed, only with view change (go to an other and then back)
})

Button(action: {
    isOn = !isOn
}, label: {
    Text(isOn ? "Add" : "Remove") // automatically refreshed
})

Update:

Sorry I missed the ItemCategory

class ItemCategory: Codable, Equatable, Identifiable, Hashable {
    var name: String
    var items: [Item]
    var chose: Bool
    var collapsed: Bool
}
4
  • 1
    it must be working, your codes has no issue, can you show struct of ItemCategory. Commented Mar 3, 2021 at 22:29
  • 1
    Without a Minimal Reproducible Example it is impossible to help you troubleshoot. Commented Mar 3, 2021 at 22:35
  • 1
    Is ItemCategory a class? Please add the code for it. Commented Mar 3, 2021 at 22:54
  • Is Itemcategory struct or a class? If it's a class it wouldn't work with @State wrapper. Change to ObservedObject wrapper. Commented Mar 4, 2021 at 5:54

3 Answers 3

1

The issue occurs because ItemCategory is a class. Whenever you change its properties, the object remains the same. The @State property wrapper reacts when the wrapped object is changed, not when only its properties are changed.

Here you can find more information about the difference between a class and a struct:


The simplest solution to your problem is to change ItemCategory to be a struct (possibly change Item as well):

struct ItemCategory: Codable, Equatable, Identifiable, Hashable {
    var name: String
    var items: [Item]
    var chose: Bool
    var collapsed: Bool

    // ...
}

Alternatively, if you want ItemCategory to remain a class, you can remove the category object and insert it again to the collection:

Button(action: {
    let category = categories.remove(at: 1)
    category.chose.toggle()
    categories.insert(category, at: 1)
}, label: {
    Text(categories[1].chose ? "Add" : "Remove")
})
Sign up to request clarification or add additional context in comments.

Comments

0

Better - use state object

@StateObject var categories: Categories = ...

Where:

class Categories : ObservableObject {
    @Published var list: [ItemCategory] = []
    init() {...}

    update(index: Int, row: ItemCategory) {
        self.objectWillChange.send()

        list[index] = row
    }
}

And to update view from @Published:

objectWillChange.send()

You can make changes already in view:

Button(action: {
    categories.objectWillChange.send()

    categories.list[1].chose = !categories.list[1].chose
}, label: {
    Text(categories[1].chose ? "Add" : "Remove") // not automatically refreshed, only with view change (go to an other and then back)
})

1 Comment

Why using both objectWillChange.send() and @published to observe one property? Also, you are calling objectWillChange.send() before appending to array.
0

For refreshing UI (observe value and update on it)

import Combine

Create a preview:

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView(list: CategoryList())
    }
}

Create a custom model class for category

class ItemCategory {
     var chose: Bool = false
}

OR create a model structure for category, don't require to send object change notification

struct ItemCategory {
    var chose: Bool = false
}

Create a static list array observing value (you can assign your data list)

class CategoryList: ObservableObject {
    @Published var items = [ItemCategory(),ItemCategory()]
}

Button text updating by observing in content view

struct ContentView: View {
    @ObservedObject var list: CategoryList
    var body: some View {
        Button(action: {
            list.items[1].chose = !list.items[1].chose
            /// If using a custom model as class, then we have to send notification to update otherwise commenting this line
            list.objectWillChange.send() // send manually update notification cos class is not auto update support
        }, label: {
            Text(list.items[1].chose ? "Add" : "Remove")
        })
    }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.