Home > Blockchain >  How to pass optional view to another view in SwiftUI?
How to pass optional view to another view in SwiftUI?

Time:11-26

So I have this TestView which accepts headerContent and bodyContent,

struct TestView<Content: View>: View {
  let headerContent: (() -> Content)?  = nil
  let bodyContent: () -> Content
  
  var body: some View {
    VStack {
      headerContent?()
      bodyContent()
    }
  }
}

And I use it as,

struct ContentView: View {  
  var body: some View {
    TestView {
      Text("Body Content")
    }
  }
}

Now, how do I pass headerContent? I tried doing,

struct ContentView: View {  
  var body: some View {
    TestView(headerContent: {
      Text("HeaderContent")
    }) {
      Text("BodyContent")
    }
  }
}

I get error,

Extra arguments at positions #1, #2 in call
Generic parameter 'Content' could not be inferred

CodePudding user response:

Change your optional variable from let to var.

When you define a variable using let keyword then you can't change it. In your example, you have set nil as constant for a headerContent.

struct TestView<Content: View>: View {
    var headerContent: (() -> Content)?  = nil
    let bodyContent: () -> Content

You can also use let with init

struct TestView<Content: View>: View {
    let headerContent: (() -> Content)?
    let bodyContent: () -> Content
    
    init(headerContent: (() -> Content)?  = nil, bodyContent: @escaping () -> Content) {
        self.headerContent = headerContent
        self.bodyContent = bodyContent
    }

CodePudding user response:

Just make it var (ie. changeable) instead of let, like

struct TestView<Content: View>: View {
  var headerContent: (() -> Content)?  = nil     // << here !!

...

CodePudding user response:

Way1:

Here is a way, but you would get issue and error if your headerView type be deferent than the body Content! E.g. Circle and Text. I solved this issue in my second Way:

struct TestView<Content: View>: View {
    
    @ViewBuilder let headerContent: (() -> Content)?
    @ViewBuilder let bodyContent: () -> Content
    
    init(headerContent: (() -> Content)? = nil, bodyContent: @escaping () -> Content) {
        self.headerContent = headerContent
        self.bodyContent = bodyContent
    }
    
    var body: some View {
        
        return VStack {
            
            headerContent?()
            bodyContent()
        }
        
    }
    
}

Way2:

Here is the right way for you:

struct TestView<BodyContent: View, HeaderContent: View>: View {
    
    @ViewBuilder let headerContent: () -> HeaderContent
    @ViewBuilder let bodyContent: () -> BodyContent
    
    init(headerContent: @escaping () -> HeaderContent, bodyContent: @escaping () -> BodyContent) {
        self.headerContent = headerContent
        self.bodyContent = bodyContent
    }
    
    init(bodyContent: @escaping () -> BodyContent) where HeaderContent == EmptyView {
        self.headerContent = { EmptyView() }
        self.bodyContent = bodyContent
    }
    
    var body: some View {
        
      return VStack {
            
            headerContent()
            
            bodyContent()
        }
        
    }
}

Use case:

struct ContentView: View {
    
    var body: some View {
          
        TestView(headerContent: {
            Circle().fill(Color.red).frame(width: 25, height: 25)
        }, bodyContent: {
            Text("Hello, World!")
        })
            

        TestView(bodyContent: {
            Text("Hello, World!")
        })

    }
    
}
  • Related