swiftswiftuiparametersviewbuilder

SwiftUI: Unable to pass View into CustomView using Parameters


i'm currently having issues with sending a custom view through into another view as a parameter. All the other parameters work perfectly, it's just the view.

I've setup everything within a single swift file to encapsulate the enviroment to trouble shoot but i just get: Cannot convert value of type 'Text' to expected argument type 'some View'

I'm most likely just missing something obvious, but i've tried with both any/some View, as well as variants with and without the @ViewBuilder but can't seem to find a solution.

Here's my code:

//
//  ProductsTabButton.swift
//  RM Product Guide
//
//  Created by Matthew Hardiman on 04/07/2023.
//

import SwiftUI

struct ProductsTabButton: View {
    
    @State var sheetActiveBool: Bool = false
    var dismissFunction: () -> Void
    var buttonColor: String = "rmWhite"
    var buttonText: String = "Button Text"
    var buttonIcon: String = "list.dash"
    var buttonView: some View = BackgroundView()
    
    
    var body: some View {
        
        // BUTTON STYLING
        Button(action: {
            sheetActiveBool.toggle()
        },label:{
            VStack(spacing: 0){
                Image(systemName: buttonIcon).productLinkBlockImage()
                Text(buttonText).productLinkBlockText()
            }
            .frame(maxWidth: .infinity, maxHeight: .infinity)
            .background(Color(buttonColor))
        })
        .sheet(isPresented: $sheetActiveBool, onDismiss: dismissFunction) {
            fetchView()
        }
        
    }
    
    
    @ViewBuilder
    func fetchView() -> some View {
         buttonView
    }
}

struct ProductsTabButton_Previews: PreviewProvider {
    static var previews: some View {
        ProductsTabButton(
            dismissFunction: {
                print("Test")
            },
            buttonColor: "rmYellow",
            buttonText: "Yellow",
            buttonIcon: "list.star",
            //buttonView: SearchView()
            buttonView: Text("Test")
        )
    }
}

Any help with this would be greatly appreciated.

Thank you!


Solution

  • You should not have Views as properties of Views. If it's a customisable part of a bigger view, store a closure that returns a view instead.

    struct ProductsTabButton<ButtonView: View>: View {
        
        @State var sheetActiveBool: Bool = false
        var dismissFunction: () -> Void
        var buttonColor: String
        var buttonText: String
        var buttonIcon: String
        var buttonView: () -> ButtonView
    
        // ...
    }
    

    You can pass this closure to sheet directly:

    .sheet(isPresented: $sheetActiveBool, onDismiss: dismissFunction, content: buttonView)
    

    If you want a default value for this property, write an initialiser in an extension:

    struct ProductsTabButton<ButtonView: View>: View {
        
        // ...
    
        init(buttonColor: String = "rmWhite", buttonText: String = "Button Text", buttonIcon: String = "list.dash", @ViewBuilder buttonView: @escaping () -> ButtonView, dismiss: @escaping () -> Void) {
            self.buttonColor = buttonColor
            self.buttonIcon = buttonIcon
            self.dismissFunction = dismiss
            self.buttonText = buttonText
            self.buttonView = buttonView
        }
    
        // ...
    }
    
    extension ProductsTabButton where ButtonView == BackgroundView {
        init(buttonColor: String = "rmWhite", buttonText: String = "Button Text", buttonIcon: String = "list.dash", dismiss: @escaping () -> Void) {
            self.init(buttonColor: buttonColor, buttonText: buttonText, buttonIcon: buttonIcon, buttonView: { BackgroundView() }, dismiss: dismiss)
        }
    }