The main View of the App is TabView with 2 screens.
I need to create an Onboarding View (there are a lot of those) and they are in their own NavigationStack. The Onboarding View shows only at the first launch.
It would be great for design requirements if i could put Onboarding View like item of TabView, with "0" tag, but without icon displaying on tapbar.
I mean, when opens my TabView, there should be no OnboardingView in it(which is .tag(0)). But i can set logic of app to open on .tag(0) on first launch, and .tag(1) on every next.
Of course, i would hide tapBar on OnboardingView.
TabView(selection: $vm.tabSelection,
content: {
OnboardingView()
.tag(0)
FirstView()
.tabItem {
Image(systemName: "sparkles")
Text("SecondView")
}
.tag(1)
SecondView()
.navigationTitle("SecondView")
.tabItem {
Text("Remove BG")
Image(systemName: "eraser.line.dashed")
}
.tag(2)
}
...
As I was suggesting in a comment, you could switch between the onboarding view and the TabView
by using a ZStack
as the top-level parent view.
You said that this wouldn't work because you are using popovers and popover tips in FirstView
. The standalone example below demonstrates that in fact it can work.
The only issue I came across was that the popover tip was not shown with the right width when using a .move
transition between onboarding and TabView
. But when a (default) opacity transition is used, it works fine.
struct ContentView: View {
@State private var onboardingDone = false
@State private var tabSelection = 1
var body: some View {
ZStack {
if onboardingDone {
TabView(selection: $tabSelection) {
FirstView(onboardingDone: $onboardingDone)
.tabItem {
Image(systemName: "sparkles")
Text("SecondView")
}
.tag(1)
SecondView()
.tabItem {
Text("Remove BG")
Image(systemName: "eraser.line.dashed")
}
.tag(2)
}
} else {
OnboardingView(onboardingDone: $onboardingDone)
}
}
}
}
struct OnboardingView: View {
@Binding var onboardingDone: Bool
var body: some View {
NavigationStack {
NavigationLink("Go to next") {
VStack(spacing: 50) {
Text("Onboarding completed")
Button("Ready for FirstView") {
withAnimation {
onboardingDone = true
}
}
.buttonStyle(.bordered)
}
}
.navigationTitle("OnboardingView")
}
}
}
struct FirstView: View {
@Binding var onboardingDone: Bool
struct QuickTip: Tip {
var title: Text {
Text("Want to onboard again?")
}
var message: Text? {
Text("The button to reset the onboarding is in the next view")
}
var image: Image? {
Image(systemName: "hand.thumbsup.circle")
}
}
var body: some View {
NavigationStack {
NavigationLink("Go to next") {
VStack(spacing: 50) {
Text("Child of FirstView")
Button("Reset onboarding") {
withAnimation {
onboardingDone = false
}
}
.buttonStyle(.bordered)
}
}
.popoverTip(QuickTip())
.onAppear {
try? Tips.resetDatastore()
try? Tips.configure()
}
.navigationTitle("FirstView")
}
}
}
struct SecondView: View {
@State private var isShowingPopover = false
var body: some View {
NavigationStack {
NavigationLink("Go to next") {
VStack(spacing: 50) {
Text("Child of SecondView with Popover")
.onTapGesture {
isShowingPopover.toggle()
}
.popover(isPresented: $isShowingPopover, arrowEdge: .bottom) {
Text("Interesting popover")
.padding()
.presentationCompactAdaptation(.popover)
}
}
}
.navigationTitle("SecondView")
}
}
}