Search code examples
swiftuiswiftui-environmentswiftui-sheetswiftui-toolbar

SwiftUI: Logout and switch screens from a popover


In my app, I have a View which is set to either a login View or a home TabView, depending on if the user is logged in. From the TabView, the user can go to a profile popover and logout. I want to switch back to the login View from this popover.

I tried dismissing the popover and immediately logging the user out, but when I test on a real device, what happens is the popover stays on the screen and also no longer responds to user input. It can't be dismissed. I'm not sure why, and what should I do instead?

Starting View:

struct StartView: View {
    @EnvironmentObject var authService:AuthService
    
    var body: some View {
        ZStack {
            if(!authService.signedIn) {
                LoginView()
            } else {
                HomeView()
            }
        }
    }
}

Home TabView:

import SwiftUI

struct HomeView: View {
    @State private var showingProfilePopover:Bool = false
    
    var body: some View {
        TabView {
            NavigationView {
                VStack(alignment: .leading) {
                    Text("Tab 1")
                    .padding(.leading, 30)
                }
                .toolbar {
                    ToolbarItem {
                        Button(action: {
                            showingProfilePopover = true
                            }, label: {
                                Image(systemName: "person.crop.circle").imageScale(.large)
                            }
                        )
                    }
                }
                
            }.popover(isPresented: $showingProfilePopover) {
                ProfileView(isPresented: $showingProfilePopover)
            }
            .tabItem {
                Image(systemName: "list.bullet")
                    .font(.system(size: 26))
                Text("Tab 1")
            }
            
            
            
            NavigationView {
                VStack(alignment: .leading) {
                    Text("Tab 2")
                }
            }.tabItem {
                Image(systemName: "books.vertical")
                    .font(.system(size: 26))
                Text("Tab 2")
            }
        }
        
    }
}

Popover:

struct ProfileView: View {
    @EnvironmentObject var authService:AuthService
    @Binding var isPresented: Bool
    
    var body: some View {
        Button("Logout") {
            // Close the popup and switch to LoginView
            print("Tapped logout")
            isPresented = false
            
            authService.signOut()
        }
        .font(Font.custom("OpenSans-Regular", size: 18))
            .padding(20)
    }
}

LoginView:

import SwiftUI

struct LoginView: View {
    @EnvironmentObject var authService:AuthService
    
    var body: some View {
        VStack {
            Button("Login") {
                self.authService.signIn()
            }.buttonStyle(.borderedProminent)
        }
    }
}

AuthService:

import SwiftUI

class AuthService: ObservableObject {
    @Published var signedIn:Bool
    
    init(signedIn:Bool) {
        self.signedIn = signedIn
    }
    
    func signIn() {
        self.signedIn = true
    }
    
    func signOut(){
        self.signedIn = false
    }
}


Solution

  • Seems like an issue connected to .popover. I can reproduce the issue, but it works just fine using .sheet instead.

    Consider attaching the .popover on the TabView or the Button itself then it seems to work just fine.