Search code examples
swiftuiviewbuilder

Swiftui How to pass a view that takes a parameter to another view that will set the parameter when calling the view


Here is a simple example, but I cannot figure out how to pass the value of test into the passed in destination view. All the examples on the web have the view "Fully" formed when it gets passed in.

( I am thinking of playing around with a function that I pass back to the Parent with the value and have it returned the formed view but that is ass backwards. )

This is to support a swift package where one of the views is supplied by the application using the package. (My proposed solution would then have the application support the GetView function. I would prefer the application just pass in a view that takes a defined parameter instead.

import SwiftUI

struct DestinationLinkView: View {
    let title: String
    var body: some View {
        Text(title)
    }
}


struct ContentView2<LinkView: View>: View {
    let linkView: LinkView
    let testList = ["Test1", "Test2", "Test3"]
    var body: some View {
        HStack {
            NavigationView {
                List {
                    ForEach(testList, id: \.self) { test in
                                                  /*====*/
                        /* *******
                        I want to pass test into self.linkView
                        but cannot figure out how.
                        */
                    NavigationLink(destination: self.linkView) {Text(test)}
// Something like
//                  NavigationLink(destination: self.linkView(title: test)) {Text(test)}
// but that does not work.
                }
            }
        }
    }
}

init(@ViewBuilder linkView: ()->LinkView) {
        self.linkView = linkView()
    }
}

struct ContentView1: View { 
    var body: some View {
        ContentView2() {
            // I want title to be set in ContentView2 when it
            // calls the desitnation
            DestinationLinkView(title: "LINK")
        }
   }
}       
struct ContentView: View {
    var body: some View {
        ContentView1() // calling here so that I don't have to change @main in another file
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

Solution

  • You can pass parameter as view builder argument.

    Here is possible approach (tested with Xcode 12.1 / iOS 14.1)

    struct ContentView2<LinkView: View>: View {
        let linkView: (String)->LinkView
        let testList = ["Test1", "Test2", "Test3"]
        var body: some View {
            HStack {
                NavigationView {
                    List {
                        ForEach(testList, id: \.self) { test in
                            NavigationLink(destination: self.linkView(test)) {Text(test)}
                        }
                    }
                }
            }
        }
        
        init(@ViewBuilder linkView: @escaping (String)->LinkView) {
            self.linkView = linkView
        }
    }
    
    struct ContentView1: View {
        var body: some View {
            ContentView2() { value in
                DestinationLinkView(title: value)
            }
       }
    }