skip to Main Content

I am building a custom tab bar in SwiftUI. The views are attached to tab bar buttons and load without any issues. But the data stored in the @State variable in the screen resets while tapping on the button.

struct ContentView: View {
    @State var index = 0
    let tabs: [AnyView]
    
    init() {
        print("### Contentview initialized ")
        tabs = [AnyView(WelcomeView(count: 0)),
                AnyView(ProfileScreen(count: 0))]
    }
    
    var body: some View {
        VStack(spacing: 0) {
            ZStack {
                tabs[index]
            }
            CustomTabbar(index: self.$index)
        }
        .navigationBarBackButtonHidden(true)
    }
}

    struct CustomTabbar: View {
    
    @Binding var index: Int
    
    var body: some View {
        HStack(alignment: .bottom) {
            Button(action: {
                self.index = 0
            }) {
                VStack(spacing: 6) {
                    Image(systemName: "house")
                    Text("Welcome")
                        .font(.system(size: 12))
                }
                .foregroundColor(index == 0 ? .blue : .black)
            }
            
            Spacer(minLength: 15)
            
            Button(action: {
                self.index = 4
            }) {
                VStack(spacing: 6) {
                    Image(systemName: "person")
                    Text("My Profile")
                        .font(.system(size: 12))
                }
                .foregroundColor(index == 4 ? .blue : .black)
            }
        }
        .frame(height: 50)
        .padding(.horizontal, 25)
        .background(Color.white)
    }
}


struct WelcomeView: View {
    @State private var count: UInt = 0
    @State private var firstAppear = false
    
    init(count: UInt, firstAppear: Bool = false) {
        print("WelcomeView init")
        self.count = count
        self.firstAppear = firstAppear
    }
    
    var body: some View {
        VStack {
            Spacer()
            Text("Welcome view")
            Spacer()
        }.onAppear {
            count += 1
            print("WelcomeView appear",count)
        }
    }
}

struct ProfileScreen: View {
    
    @State private var count: UInt = 0
    
    init(count: UInt) {
        print("profile screen init")
        self.count = count
    }

    var body: some View {
        VStack {
            Spacer()
            Text("Profile screen")
            Spacer()
        }.onAppear {
            count += 1
            print("Profile view appear",count)
        }
    }
}

Screenshot of the app

Whenever the tab button is clicked, the count in that view resets to 0, but the init method is called once. Why is this happening? How can I preserve the state variable during the tab switch?

TIA

2

Answers


  1. It has been a while since I used swift UI, but I believe the issue is the fact that you use tabs[index] in order to switch between components. Although you are creating a single instance of each view, the view’s body does not behave like a single instance. @State is not persistent between renders, hence the state disappears. You should hoist the variables into the ContentView and use a binding to make the state persistent since the ContentView will not need to rerender. Another solution is using something like MVVM in order to more professionally manage state in your application.

    Login or Signup to reply.
  2. This can help?

    import SwiftUI
    
    struct ContentView: View {
        @State var index = 0
        @State private var count1: UInt = 0
        @State private var count2: UInt = 0
        
        var body: some View {
            VStack(spacing: 0) {
                ZStack {
                    switch index {
                    case 0:
                        WelcomeView(count: $count1)
                    default:
                        ProfileScreen(count: $count2)
                    }
                }
                CustomTabbar(index: self.$index)
            }
            .navigationBarBackButtonHidden(true)
        }
    }
    
        struct CustomTabbar: View {
        
        @Binding var index: Int
        
        var body: some View {
            HStack(alignment: .bottom) {
                Button(action: {
                    self.index = 0
                }) {
                    VStack(spacing: 6) {
                        Image(systemName: "house")
                        Text("Welcome")
                            .font(.system(size: 12))
                    }
                    .foregroundColor(index == 0 ? .blue : .black)
                }
                
                Spacer(minLength: 15)
                
                Button(action: {
                    self.index = 4
                }) {
                    VStack(spacing: 6) {
                        Image(systemName: "person")
                        Text("My Profile")
                            .font(.system(size: 12))
                    }
                    .foregroundColor(index == 4 ? .blue : .black)
                }
            }
            .frame(height: 50)
            .padding(.horizontal, 25)
            .background(Color.white)
        }
    }
    
    
    struct WelcomeView: View {
        @Binding var count: UInt
        @State private var firstAppear = false
        
        init(count: Binding<UInt>, firstAppear: Bool = false) {
            print("WelcomeView init")
            self._count = count
            self.firstAppear = firstAppear
        }
        
        var body: some View {
            VStack {
                Spacer()
                Text("Welcome view")
                Spacer()
            }.onAppear {
                count += 1
                print("WelcomeView appear",count)
            }
        }
    }
    
    struct ProfileScreen: View {
        @Binding var count: UInt
        
        init(count: Binding<UInt>) {
            print("profile screen init")
            self._count = count
        }
    
        var body: some View {
            VStack {
                Spacer()
                Text("Profile screen")
                Spacer()
            }.onAppear {
                count += 1
                print("Profile view appear",count)
            }
        }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search