简体   繁体   中英

How to pass optional view to another view in SwiftUI?

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

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
    }

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

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

...

Way1:

Here is a way, but you would get issue and error if your headerView type be deferent than the body Content. Eg 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!")
        })

    }
    
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM