4

This is more of a syntactic question. I am trying to pass a view into a ViewModifier initialiser without using AnyView.

Currently I have something running using AnyView, but I would like to use the generic view syntax as you would then passing a "some view" to a view outlined in this answer here:

How to pass one SwiftUI View as a variable to another View struct

I cant seem to get it working with a ViewModifier.

The intention (as shown in the sample) is for the modifier to pass in a computed value into the innerView. I would also be happy with using @ViewBuilder somehow to specify the innerView property as this would probably have the same effect.

Here is the sample modifier:

struct SampleModifier: ViewModifier {

    let param1: CGFloat
    let innerView:  (CGFloat, CGFloat) -> AnyView
    
    // Assume these are calculated states
    let calculatedParam1: CGFloat = 100
    let calculatedParam2: CGFloat = 100
    
    func body(content: Content) -> some View {
        ZStack {
            HStack {
                Spacer()

                innerView(self.calculatedParam1, self.calculatedParam2)
                    .padding(.leading, 10)
                    .frame(width: param1)
            }
            
            content.frame(width: 100, height: 100)
        }
        
    }
}

And its use:

struct SampleView: View {
    var body: some View {
        Rectangle()
            .modifier(SampleModifier(param1: 150, innerView: { param1, param2 in
                return AnyView(
                    Text("Inner View: \(param1) - \(param2)")
                )
            }))
    }
}
pawello2222
  • 46,897
  • 22
  • 145
  • 209
Slappy
  • 4,042
  • 2
  • 29
  • 41

1 Answers1

6

You can use generics, i.e., make innerView returning some view:

struct SampleModifier<V>: ViewModifier where V: View {
    let param1: CGFloat
    let innerView: (CGFloat, CGFloat) -> V

    ...
}

Then you don't need AnyView anymore:

struct SampleView: View {
    var body: some View {
        Rectangle()
            .modifier(SampleModifier(param1: 150, innerView: { param1, param2 in
                Text("Inner View: \(param1) - \(param2)")
            }))
    }
}

Alternatively, you can create a View extension:

extension View {
    func sampleModifier<V>(
        param1: CGFloat,
        innerView: @escaping (CGFloat, CGFloat) -> V
    ) -> some View where V: View {
        modifier(SampleModifier(param1: param1, innerView: innerView))
    }
}

and use it like this:

struct SampleView: View {
    var body: some View {
        Rectangle()
            .sampleModifier(param1: 150, innerView: { param1, param2 in
                Text("Inner View: \(param1) - \(param2)")
            })
    }
}
pawello2222
  • 46,897
  • 22
  • 145
  • 209
  • Brilliant, this is EXACTLY what I was looking for. One more question, if I were to do a view extension helper function, what would it look like? I am really battling with syntax when combining generics and protocols. – Slappy Dec 22 '20 at 00:08
  • 2
    @Slappy I updated my answer with a View extension. – pawello2222 Dec 22 '20 at 00:23