如何通过函数为TabView传入内容

How to pass in content for TabView through a function

我正在尝试构建一个可重复使用的入职寻呼机叠加层。所以我想根据变量 IntroType 将不同的“幻灯片”传递到寻呼机 TabView 中。但是,由于 TabView 无需任何容器即可直接获取内容,因此 return 类型是什么?

这就是我希望它的工作方式:

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)
    }
}


另一种方法是将扩展 return 中的每个变量作为一个整体 TabView,但这非常丑陋,我也没有完全弄明白,仍然会遇到一些其他错误那条路线。

我认为必须有 一些 方法来做到这一点,我只是还不太了解 SwiftUI 的可用工具。我可以想象一个带有 @ViewBuilder 包装器的函数可以在这里使用,但到目前为止我还没有完全理解它背后的逻辑。

我最接近的是使用一组 AnyView() 幻灯片的向下转换,但这使得使用 ForEach 变得困难并删除了类型。

你可以这样传递内容:

    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)
          }
      
        }
      }
    }

在@Chris 的帮助下,我使其工作如下:

import SwiftUI


struct IntroView: View {
    @Binding var isPresented: Bool
    var introType: IntroType

    var body: some View {
        if (introType == .Main) {
            getContentMain()
        }
        else if (introType == .SA) {
            getContentSA()
        }
        else if (introType == .Journey) {
            getContentJourney()
        }
    }
}

fileprivate struct IntroTabView<Content: View>: View {
    let content: Content
    @Binding var isPresented: Bool
    @State var activeSlide: Int = 0
    let slideAmount: Int
    
    init(isPresented: Binding<Bool>, slideAmount: Int, @ViewBuilder content: () -> Content) {
        self._isPresented = isPresented
        self.slideAmount = slideAmount
        self.content = content()
    }
    
    var body: some View {
        ZStack(alignment: .bottom) {
            Color.darkGalaxy
            
            TabView(selection: $activeSlide) {
                self.content
            }
            .tabViewStyle(PageTabViewStyle())
            .indexViewStyle(.page(backgroundDisplayMode: .always))
            
            // Buttons
            HStack { ... }
            .padding(16)
        }
    }
}

// Static content blocks for each type
fileprivate extension IntroView {
    private func getContentMain() -> some View {
        IntroTabView(isPresented: $isPresented, slideAmount: 3) {
            MainIntroSlide1().tag(0)
            TextSlide(headline: "Headline", text: "Text").tag(1)
            TextSlide(headline: "Headline2", text: "Text2").tag(2)
        }
    }
    
    private func getContentSA() -> some View {
        IntroTabView(isPresented: $isPresented, slideAmount: 2) {
            TextSlide(headline: "Headline", text: "Text").tag(0)
            TextSlide(headline: "Headline2", text: "Text2").tag(1)
        }
    }
    
    private func getContentJourney() -> some View {
        IntroTabView(isPresented: $isPresented, slideAmount: 1) {
            TextSlide(headline: "Headline", text: "Text").tag(0)
        }
    }
}

enum IntroType: String {
    case Main, SA, Journey
}

struct IntroView_Previews: PreviewProvider {
    static var previews: some View {
        IntroView(isPresented: .constant(true), introType: .Main)
    }
}

产生了这个美丽的景色: