SwiftUI - View using Enum not updating

Viewed 579

Firstly, I want to say the value updates, as I print the value(s) in the console and sure, tapping each option prints as expected. However, for UI purposes, I have added a few visual components/styles to help with indicating the current selection.

My enum:

enum Gender : Int, CaseIterable {
    case men = 0
    case women = 1
  

    private var cases: [String]  {
        ["Men", "Women"]
    }
   
    func toString() -> String {
        cases[self.rawValue]
    }
}

This is the view that helps with the logic for displaying the data and indexing the data

struct GenderTabMenuIndicator: View {
    var category: Gender
    var body: some View {
        HStack {
            ForEach(0..<Gender.allCases.count) { cat in
                GenderTabMenuIndicatorItem(category: Gender.allCases[cat], isActive: Gender.allCases[cat] == category)
                
            }
        }.frame(width: UIScreen.main.bounds.width * 0.75)
    }
}

And this is simply the view. However, the isActive does not seem to switch from the initial selection/value.

struct GenderTabMenuIndicatorItem: View {
    @State var category: Gender
    @State var isActive: Bool
    var body: some View {
        VStack(spacing: 0) {
            Text(category.toString().uppercased())
                .onTapGesture {
                    print("tapped")
                    print(category.toString())
                }
                .font(.system(size: 18, weight: isActive ? .bold : .light))
                .frame(maxWidth: .infinity)
                .layoutPriority(1)
            if isActive {
                Rectangle()
                    .frame(width: 50, height: 2, alignment: .center)

            }
        }.foregroundColor(Color(SYSTEM_FONT_COLOUR))
    }
}

This is how I'm declaring/using all these components in my actual view:

@State private var selected_tab: Gender = .men

VStack {
GenderTabMenuIndicator(category: selected_tab)
}

I don't know if it's the ForEach loop, but that at the same time does print the corresponding case that's passed. I have used @State where I can to update the view, but to no luck.

Any help would be appreciated!

1 Answers
  • @State is used for private changes, inside withing a view

  • to update changes back and forth from sub view you have to use @Binding

  • we can access/pass binding of @State using $ , ex :- $yourStateVariable

Here is the Fixed answer ,

struct ContentView: View {
        @State private var selected_tab: Gender = .men
        
        var body: some View {
            VStack {
                Text("Selected: \(selected_tab.toString())")
                GenderTabMenuIndicator(category: $selected_tab)
            }
        }
    }

    enum Gender : Int, CaseIterable {
        case men = 0
        case women = 1
        
        
        private var cases: [String]  {
            ["Men", "Women"]
        }
        
        func toString() -> String {
            cases[self.rawValue]
        }
    }
    
    struct GenderTabMenuIndicator: View {
        @Binding var category: Gender
        var body: some View {
            HStack {
                ForEach(0..<Gender.allCases.count) { cat in
                    GenderTabMenuIndicatorItem(category: Gender.allCases[cat], selection: $category)
                    
                }
            }.frame(width: UIScreen.main.bounds.width * 0.75)
        }
    }
    
    struct GenderTabMenuIndicatorItem: View {
        var category: Gender
        @Binding var selection: Gender
        var body: some View {
            VStack(spacing: 0) {
                Text(category.toString().uppercased())
                    .onTapGesture {
                        selection = category
                    }
                    .font(.system(size: 18, weight: selection == category ? .bold : .light))
                    .frame(maxWidth: .infinity)
                    .layoutPriority(1)
                if selection == category {
                    Rectangle()
                        .frame(width: 50, height: 2, alignment: .center)
                    
                }
            }
        }
    }
Related