I am trying to build a reusable onboarding pager overlay. So I want to pass in different "slides" into the pager TabView
based on a variable IntroType
. But since TabView
takes content plainly without any container, what would be the return type for that?
This is how I wish it worked:
struct IntroViewTabPageTest: View {
// MARK: Variables
@Binding var isPresented: Bool
@State var activeSlide: Int = 0
var introType: IntroType
// MARK: UI
var body: some View {
ZStack(alignment: .bottom) {
Color.darkGalaxy
TabView(selection: $activeSlide) {
getContentBasedOnType() // <---- Content here
}
.tabViewStyle(.page)
.indexViewStyle(.page(backgroundDisplayMode: .always))
}
}
// MARK: Functions
private func getContentBasedOnType() -> some View {
switch (introType) {
case .Main:
return introContentMain
case .SA:
return introContentSA
case .Journey:
return introContentJourney
}
}
}
// Static content blocks for each type
extension IntroViewTabPageTest {
// ----> Of course everywhere errors here, "some View" is not the right return type;
// But what is it?
private var introContentMain: some View {
MainIntroSlide1()
TextSlide(headline: "Headline", text: "Text")
}
private var introContentSA: some View {
TextSlide(headline: "Headline", text: "Text")
TextSlide(headline: "Headline2", text: "Text2")
TextSlide(headline: "Headline3", text: "Text3")
}
private var introContentJourney: some View {
TextSlide(headline: "Headline", text: "Text")
}
}
enum IntroType: String {
case Main, SA, Journey
}
struct IntroViewTabPageTest_Previews: PreviewProvider {
static var previews: some View {
IntroViewTabPageTest(isPresented: .constant(true), introType: .Main)
}
}
An alternative would be to have each variable in the extension return a whole TabView
, but that's very ugly and I haven't figured it out fully either, still getting some other error with that route.
I assume that there must be some way to do this and I just don't know the available tools of SwiftUI well enough yet. I can imagine a function with a @ViewBuilder wrapper could be of use here, but I don't fully understand the logic behind it so far.
The closest I've come was using an array of AnyView() downcasts of the slides, but that made using a ForEach hard and erased the types.
You could pass the content in like this:
import SwiftUI
struct IntroViewTabPageTest<Content: View>: View {
let content: Content
init(@ViewBuilder content: () -> Content) {
self.content = content()
}
var body: some View {
ZStack(alignment: .bottom) {
Color.gray
TabView() {
self.content
}
.tabViewStyle(.page)
.indexViewStyle(.page(backgroundDisplayMode: .always))
}
}
}
struct IntroViewTabPageTest_Previews: PreviewProvider {
static var previews: some View {
Group {
IntroViewTabPageTest {
Text("hello 1")
Text("hello 2")
}
IntroViewTabPageTest {
Rectangle()
.fill(Color.green)
.frame(width: 100, height: 100)
}
}
}
}