MattBlack
MattBlack

Reputation: 3828

SwiftUI list not updating on observable object change

I am attempting to create an 'expandable menu'.

So far I have created a 'menuArray' which contains a list of the headings and also an array of sub headings.

struct menuStruct: Identifiable {
    var id: Int
    var text: String
    var isExpanded: Bool
    var subItems: [subMenuStruct]?
}

struct subMenuStruct {
    var text: String
    var selected: Bool
    var link: pagesEnum
}

When the user taps the row, I am toggling the menuArray.isExpanded value.

The issue I have is that when the row is tapped, the UI is not being updated. The print statement shows that the array has been correctly changed but for what ever reason its not being reflected in the list.

Initially I was holding the array as a 'State' object but read that the changing of a value within an element is not considered a change, therefore I should use an observable object. This doesn't seem to have helped.

Why aren't the changes the the array values being reflected in the UI?

thanks!

class MenuViewModel: ObservableObject {
    @Published var menuArray: [menuStruct] = [menuStruct(id: 1, text: "SMS", isExpanded: false, subItems: [subMenuStruct(text: "Stats", selected: false, link: .smsStats),subMenuStruct(text: "Customers", selected: false, link: .smsCustomers),subMenuStruct(text: "Activity", selected: false, link: .smsActivity),subMenuStruct(text: "Customer Care", selected: false, link: .smsCutomerCare),subMenuStruct(text: "Keywords", selected: false, link: .smsKeywords)]),menuStruct(id: 2, text: "Online Chat", isExpanded: false, subItems: [subMenuStruct(text: "Stats", selected: false, link: .ocStats),subMenuStruct(text: "Customers", selected: false, link: .ocCustomers),subMenuStruct(text: "Activity", selected: false, link: .ocActivity)]),menuStruct(id: 3, text: "Console", isExpanded: true, subItems: [subMenuStruct(text: "Queues", selected: false, link: .queues)]),menuStruct(id: 4, text: "Support", isExpanded: false, subItems: [subMenuStruct(text: "Contact Admin", selected: false, link: .support)])]

}

struct Menu: View {

    @ObservedObject var menuVM = MenuViewModel()
    @EnvironmentObject var settings: UserSettings

    var body: some View {

        GeometryReader { geo in

            VStack(spacing: 0){

                Spacer().frame(height: geo.safeAreaInsets.top)

                HStack(spacing:0){
                    Text("AllStar.").font(.custom("Poppins-Bold", size: 39))

                    Spacer()
                    Image("Cross").resizable().frame(width:20, height: 20).onTapGesture {
                        self.settings.menuShowing = false
                    }
                }.padding(.horizontal,20)

                HStack(spacing:0){

                    Text("Chat").font(.custom("Poppins-Bold", size: 39)).foregroundColor(Color.init("Pink"))
                    Spacer()

                }.padding(.leading,20)


                ScrollView{

                    HStack{
                        Text("Dashboard").font(.custom("Poppins-Bold", size: 24))
                        Spacer()
                    }.padding(.top,10).padding(.horizontal,20)

                    ForEach (self.menuVM.menuArray.indices){ index in

                        HStack(spacing:0){

                            Text(self.menuVM.menuArray[index].text).font(.custom("Poppins-Bold", size: 24))

                            if(self.menuVM.menuArray[index].subItems != nil){

                                Image("downArrow").resizable().frame(width:40,height:15)
                            }

                            Spacer()
                        }.onTapGesture {

                            // Tapping the row will cause it to expand
                            self.menuVM.menuArray[index].isExpanded.toggle()
                            self.menuVM.objectWillChange.send()
                            print(String.init(describing: self.menuVM.menuArray))
                        }

                        if self.menuVM.menuArray[index].isExpanded  {
                            Text("Expanded!")
                        }

                    }.padding(.horizontal,20)
                }

                Spacer()
            }.background(Color.white).edgesIgnoringSafeArea(.all)
        }
    }
}

Upvotes: 1

Views: 594

Answers (1)

This is what I did to make things work. Change the structs to classes, because you have to change the values in them, and structs do not allow that. This is one of the reasons why the view is not updating.

class menuStruct: Identifiable {
var id: Int
var text: String
var isExpanded: Bool
var subItems: [subMenuStruct]?

init(id: Int, text: String, isExpanded: Bool, subItems: [subMenuStruct]?) {
    self.id = id
    self.text = text
    self.isExpanded = isExpanded
    self.subItems = subItems
}
}

class subMenuStruct {
var text: String
var selected: Bool
var link: pagesEnum

init(text: String, selected: Bool, link: pagesEnum) {
    self.text = text
    self.selected = selected
    self.link = link
}
}

I also change the ScrollView to:

            ScrollView{
                HStack{
                    Text("Dashboard").font(.custom("Poppins-Bold", size: 24))
                    Spacer()
                }.padding(.top,10).padding(.horizontal,20)
                ForEach (self.menuVM.menuArray){ menu in
                    HStack(spacing:0){
                        Text(menu.text).font(.custom("Poppins-Bold", size: 24))
                        if(menu.subItems != nil){
                            Image("downArrow").resizable().frame(width:40,height:15)
                        }
                        Spacer()
                    }.onTapGesture {
                        // Tapping the row will cause it to expand
                        menu.isExpanded.toggle()
                        self.menuVM.objectWillChange.send()
                        print(String.init(describing: self.menuVM.menuArray))
                    }
                    if menu.isExpanded {
                        Text(" \(menu.text)  is Expanded")
                    }
                }.padding(.horizontal,20)
            }

Upvotes: 3

Related Questions