Search code examples
iosswiftuiviewbindingstate

How to refresh an image on the main view after update it from grandchild view in SwiftUI


I have three level struct view and I couldn't figure out how to pass the image change from 3rd to 1st. I am confused with how to use @State and @Binding in my "iconValue" property. The image on the highest level view only changes after dismiss and reopen the screen.

The highest level view is "ExLibrisStyleView".It includes row categories of icon collection fed from "ExLibrisCategoryRow" view. On top of the view it shows selected icon. "ExLibrisCategoryRow" is combination of images called from "ExLibrisCategoryItem". When I tap on any image I want to change the selected icon on top of the view instantly. However it changes after main view is dismissed and reopened again. Really appreciate to show me where is the mistake which leads to image update after reopen the view.

my code for each struct view is:

ExLibrisStyleView view

    import SwiftUI

    struct ExLibrisStyleView: View {
   
    
    @EnvironmentObject var exlibrisData: ExLibrisModelData
   
    @State var iconValue = UserDefaults.standard.string(forKey: "iconValue")
   
    var body: some View {
      
        NavigationView{
                     
                List {
                    
                    
                    VStack(alignment: .leading) {
                        Text("Selected Bookmark").font(.headline).padding(.bottom,15)
         
                            Image(iconValue ?? "exlibris-0")
                                .renderingMode(.original)
                                .resizable()
                                .frame(width: 130, height: 130)
                                .cornerRadius(5)
                                .shadow(color: Color(red:0, green:0, blue:0, opacity:0.25), radius: 8, x: 0, y: 0)
                        
                    }
                    .padding(.leading, 15)
                    .padding(.bottom,30)
                    .listStyle(.inset)
                    .listRowInsets(EdgeInsets())
                
                    ForEach(exlibrisData.categories.keys.sorted(), id: \.self) { key in
                        ExLibrisCategoryRow(categoryName: key, items: exlibrisData.categories[key]!,iconValue: iconValue ?? "exlibris-0")
                            
                    }
                    .listRowInsets(EdgeInsets())
                }
                .listStyle(.inset)
                .navigationTitle("Bookmark Collection")
            }
        }
}

ExLibrisCategoryRow view:

    import SwiftUI
    struct ExLibrisCategoryRow: View {
    
    var categoryName: String
    var items: [ExLibris]
    @State var iconValue: String
    var body: some View {
              
        VStack(alignment: .leading) {
            Text(categoryName)
                .font(.headline)
                .padding(.leading, 15)
            
            ScrollView(.horizontal, showsIndicators: false) {
                HStack(alignment: .top, spacing: 0) {
                    ForEach(items) { exlibris in
                        
                    label: do {
                        ExLibrisCategoryItem(exlibris: exlibris, iconValue: $iconValue)
                    }
                    }
                }
            }
            .frame(height: 200)
        }       
    }
}

ExLibrisCategoryItem View:

    import SwiftUI

    struct ExLibrisCategoryItem: View {
    
    var exlibris: ExLibris
    
    @Binding var iconValue: String
    
    
    var body: some View {
        
        VStack(alignment: .leading) {
            
            Image(exlibris.imageName)
                .renderingMode(.original)
                .resizable()
                .frame(width: 130, height: 130)
                .cornerRadius(5)
                .onTapGesture {
         
                    print(iconValue)
                    UserDefaults.standard.set(exlibris.imageName, forKey: "iconValue")
                    
                    iconValue = UserDefaults.standard.string(forKey: "iconValue") ?? "exlibris-0"
                
                }            
        }
        .padding(.leading, 15)
    }
}

Solution

  • After spending hours how to solve the problem or change the logic behind the views, I have searched the solution in a different way and just found out an elegant wrapper called @AppStorage to update any view with UserDefaults. It is exactly the one I was looking for. I have found out it in this post and other link explains how to use.

    discussion about Userdefaults change listening

    how to use @AppStorage wrapper