Update a view from a button in another view.

Prerequisite Information


Hello,

I have a macOS app which is split between two views each having it's own rows/cells .
You can click a row in the first view and it opens a view with more rows in the second view.

I have a custom view struct, which is a button saving and receiving data from Core Data, that depending on which of the two views you call it, it has a stage, and changes colour.

The relationship between them is, a row from the first view, has multiple rows from the second view. (This applies for both the views and the Core Data entities)

What should be happening:


First view button
Normally, when you press the button on the first view, it should update the Core Data value and change it's colour. Then it should update all of the stage values of all it's rows in the second view in Core Data, and then refresh the second view so their buttons reflect the changes in Core Data.

Second view button
When you press the button in the second view, it should update it's stage value in Core Data, change it's colour, and then update the value of it's parent view in Core Data and refresh to update it's colour.

What is happening now:


What is happening now, is that it correctly updates the stages in Core Data, and also applies the correct colours on the buttons when the app initializes, but when you press the button on either the first or the second view, it only changes the colour of the button pressed. It does not refresh the other view, so it reflects the value in Core Data.

I tried adding a binding variable that i would change when the button was clicked, without results. I also tried manually changing the values in the view model, but again no success.

How could I make this work?
One way is to use notifications. So that when you change, you send notification that the other view will receive.

Or use delegation, but that seems less appropriate here (because you have relation 1 -> 2 and 2 >-1 if I understand well.
Hello Claude31,

Thanks for the answer!

I searched for how I could use notifications to refresh my view, but all I found was about push and local notifications, shown in the notification center as a feedback to the user.
I couldn't find something that would send an "internal" notification to the other view.

Do you have a sample or a website, that explains how I could implement this, and also when the view receives the notification, how would it refresh?
Do as follows:

declare a name (give what you want)
Code Block
extension Notification.Name { 
    public static let kRefresh = Notification.Name("refresh")
}


In the view that has to receive notification, add observer in viewDidLoad:
Code Block
        NotificationCenter.default.addObserver(self, selector: #selector(refresh), name: .kRefresh, object: nil) 

And create the func to handle the notification
Code Block
    @objc func refresh() {     
// Update elements you have to update in this receiving view
}


Hope that's clear.
If that's OK, don't forget to close the thread on the correct answer.
Otherwise, please tell what you miss.


The view sends notification with:
        NotificationCenter.default.post(name: .kRefresh, object: self) 


Thanks for the answer!

I tried to apply this to my code, but i couldn't make it work.

The problem is, I use SwiftUI and it's a SwiftUI app, so it does not have a viewDidLoad and it's also not a class but a struct, so i couldn't use the addObserver function.

Also, I still haven't understood how am I going to refresh my view, as it's a navigation link that points to another view that has a viewBuilder struct with a list that returns the data in a closure variable.

That view is like:

Code Block Swift
struct GroupListView: View {
        var body: some View {
                VStack {
                        /* Custom viewBuilder struct */
                FilteredList(parameters: viewModel.filterParameters) { (item: EntityItem) in
                                /* View for nicely displaying the rows */
                                /* (This view is the first one in my question description which has one of the buttons) */
                        ListRow(rowValue: item)
                }
                }
        }
}


You should have told before.

In SwiftUI, it is different. Use environment var.

I thought I had mentioned it, but apparently not. I only wrote it in the tags.

I have not understood how am I going to implement it. Could you please elaborate more on this.
This tutorial is well written and should let you do it easily with @EnvironmentObject.

https ://www.hackingwithswift. com/quick-start/swiftui/how-to-use-environmentobject-to-share-data-between-views
I already use environment objects in my code, but how will this make my view reload to get the new data from the CoreData database?
Hello Claude31,

I tried to make it work, but it didn't, so I added a workaround (which I don't really like, to be honest) which repeats some parts of the code two times based on the toggle of a Bool variable.

Here is the repository for the project: GitHub Link.

If you know some other way to properly implement this, please inform me.
If you have a work around, that's good.

I still find SwiftUI hard to use, or better say, a bit inflexible. That's probably the price to pay for the automated mechanisms that run behind the scene. But I still prefer UIKit.

Have a good day.
I tried to create an app with full SwiftUI to see how it works and experiment a bit with it, but as you note, I also found out that it's inflexible, a bit limited and kind of immature at the moment.

With UIKit you could do almost whatever you wanted, but my main issue, was with positioning the elements on the screen with constrains.

I guess next time I'll merge UIKit with a few SwiftUI components to get the best of both.

Anyway, thanks for the answers.
Contraints are not that difficult if you proceed with some method:
  • if possible, embed items that are logically linked into a view (to avoid having a lot of objects at first level of the overall view)

  • set the constraints of these views to the superView or the safe area

  • inside each of these views (let's call it view A), set constraints for objects.

  • Start from an object (O1) at the top left for instance, set its constraints relative to viewA

  • then set constraints of other objects for which that makes sense, relative to the "anchor" object O1.

  • if necessary repeat with an object O2, , set its constraints relative to viewA

  • then set constraints of other objects …

Good continuation.
Update a view from a button in another view.
 
 
Q