TEAM LICENSES: Save money and learn new skills through a Hacking with Swift+ team license >>

SOLVED: Sheet in TabView showing wrong content

Forums > SwiftUI

Hello,

I noticed a strange behavior in the TabView with Sheets. When you launch the following ContentView:

import SwiftUI

struct MealPlanView: View {
    @State var showingDetailView = false

    var body: some View {
        TabView {
            ForEach(0..<5) { index in
                Button(
                    action: {
                        showingDetailView = true
                    },
                    label: {
                        Text("\(index)")
                            .font(.largeTitle)
                    }
                )
                .sheet(isPresented: $showingDetailView) {
                    Text("\(index)")
                        .font(.largeTitle)
                }
                .buttonStyle(PlainButtonStyle())
                .padding([.bottom], 50)
                .tabItem {
                    Text("\(index)")
                }
            }
        }
    }
}
  1. Tap on the first tab "Tab 0" opens sheet "Sheet 0". OK
  2. Go to the second tab "Tab 1" and tap it. This open sheet "Sheet 1". OK
  3. Go back to the first tab "Tab 0" and tap on it. This opens sheet "Sheet 1" not "Sheet 0". NOT OK.

Can anybody confirm this behavior? If so, does anyone know the solution or a workaround.

Thanks and best regards Pawel

2      

Hi Pawel! Not sure what is the object you want to pass around but you can use TabView(selection:) initializer like so:

struct MealPlanView: View {
    @State var showingDetailView = false
    @State private var selectedTab = 0

    var body: some View {
        TabView(selection: $selectedTab) {
            ForEach(0..<5) { index in
                Button(
                    action: {
                        showingDetailView = true
                    },
                    label: {
                        Text("\(index)")
                            .font(.largeTitle)
                    }
                )
                .buttonStyle(PlainButtonStyle())
                .padding([.bottom], 50)
                .tabItem {
                    Text("\(index)")
                }
                .tag(index)
            }
        }
        .sheet(isPresented: $showingDetailView) {
            Text("\(selectedTab)")
                .font(.largeTitle)
        }
    }
}

2      

Yes I know that. But that's not the problem here. The problem is that after switching the tab back and forth the wrong content is displayed in the sheet. Actually, after tapping on the tab, the corresponding number should also be displayed in the sheet.

2      

hi Pawel,

just use one .sheet modifier for the whole TabView, not what are 5 button subviews with their own .sheet modifier.

this works:

import SwiftUI

struct MealPlanView: View {
    @State var showingDetailView = false
    @State var selectedIndex = 0

    var body: some View {
        TabView {
            ForEach(0..<5) { index in
                Button(
                    action: {
                        selectedIndex = index
                        showingDetailView = true
                    },
                    label: {
                        Text("\(index)")
                            .font(.largeTitle)
                    }
                )
                .buttonStyle(PlainButtonStyle())
                .padding([.bottom], 50)
                .tabItem {
                    Text("\(index)")
                }
            }
        }
        .sheet(isPresented: $showingDetailView) {
            Text("\(selectedIndex)")
                .font(.largeTitle)
        }

    }
}

hope that helps,

DMG

2      

It took me some time to find where I read/watched about that. I thought it was an article but turned out to be youtube video. Where the reason for that is explained. Here is the link https://www.youtube.com/watch?v=8rCtYoG9JIM, how to handle multiple sheets on the view. The solution to this is to use a model something like this:

struct MealPlanView: View {
    @State var menuItem: MenuOption?

    var body: some View {
        TabView {
            ForEach(MenuOptionViewModel.sample) { item in
                Button(
                    action: {
                        menuItem = item
                    },
                    label: {
                        Text("\(item.name)")
                            .font(.largeTitle)
                    }
                )
                .buttonStyle(PlainButtonStyle())
                .padding([.bottom], 50)
                .tabItem {
                    Text("\(item.name)")
                }
            }
        }
        .sheet(item: $menuItem) { item in
            Text("Item: \(item.name)")
        }
    }
}

struct MenuOption: Identifiable {
    let id = UUID()
    let name: Int
}

struct MenuOptionViewModel {
    static var sample = [
        MenuOption(name: 0),
        MenuOption(name: 1),
        MenuOption(name: 2),
        MenuOption(name: 3),
        MenuOption(name: 4)
    ]
}

2      

Hello,

fine. That is the solution. Thank you for your time and help guys.

BR Pavel

2      

Pawel, notice that the solution offered by @delawaremathguy is not really handling initial value passing. Just try to go to tab 4 and press the button you still will see '0' on your .sheet. This is explained in the video that I offered you to watch. The only solution I could find is creating of optional value that will trigger appearance of .sheet, but it has to be Identifiable that is why in my example I put data in struct.

2      

hi,

i agree with Yuri (@ygeras) ... i forgot this subtlety.

but you can work just with the Int type without the need to put in a separate struct if you do these three things:

(1) make Int Identifiable.

extension Int: Identifiable {
    public var id: Int { self }
}

(2) use an optional for the selected index.

    @State var selectedIndex: Int?

(3) use the .sheet(item:) modifier as Yuri suggests

        .sheet(item: $selectedIndex) { index in
            Text("\(index)")
                .font(.largeTitle)
        }

hope that helps (and more than my last attempt)

DMG

2      

Hacking with Swift is sponsored by String Catalog.

SPONSORED Get accurate app localizations in minutes using AI. Choose your languages & receive translations for 40+ markets!

Localize My App

Sponsor Hacking with Swift and reach the world's largest Swift community!

Reply to this topic…

You need to create an account or log in to reply.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.