How do I create a View Modifier that returns a view as well as an action?

209 Views Asked by At

I have created a View modifier alongside a sheet manager to deal with and create popup views throughout my project. It works perfectly with a simply popup and a close button to dismiss the view. Now what I am trying to is add more functionality to this sheet manager and modifier so that I can create/use heftier custom popups. By that I mean being able to use buttons and their actions on these popups.

view modifier code

    func popup(with sheetManager: SheetManager, action: @escaping () -> Void) -> some View {
        self.modifier(PopupViewModifier(sheetManager: sheetManager){ })
    }

function/view being called

struct PopupViewModifier: ViewModifier {
    @ObservedObject var sheetManager: SheetManager
    var popupAction: () -> (Void)
    
    
    func body(content: Content) -> some View {
        content
            .disabled(sheetManager.action.isPresented ? true : false )
            .blur(radius: sheetManager.action.isPresented ? 05 : 0)
            .overlay(alignment: .center) {
                if case let .present(config) = sheetManager.action {
                    switch config.type {
                    case .popupAlert:
                        PopupView(config: config) {
                            withAnimation {
                                sheetManager.dismiss()
                            }
                        }
                    case .soundProfileList:
                        SoundProfilePopupView(config: config) {
                            withAnimation {
                                sheetManager.dismiss()
                            }
                        } tappedProfile: {
                            popupAction()
                        }
                    }
                }
            }
            .ignoresSafeArea()
    }
}

And how its called in the main view where I am trying to get the action callback

@EnvironmentObject var sheetManager: SheetManager

var body: some View {
    buttonToCallPopup( didPress: {
        sheetManager.present(with: Config.innit)
    }) {
        Text("Open up popup")
      }
    .popup(with: sheetManager) {
      print("button from popup has been pressed")
    }
}

SheetManager code

enum SheetType {
    case popupAlert
    case soundProfileList
}
final class SheetManager: ObservableObject {
    typealias Config = Action.Info
    @Published private(set) var action: Action = .na

    enum Action {
        struct Info {
            let systemName: String
            let title: String
            let content: String
            let type: SheetType
        }
        
        case na
        case present(info: Info)
        case dismiss
    }

    func present(with config: Config) {
        guard !action.isPresented else { return }
        self.action = .present(info: config)
    }
    
    func dismiss() {
        self.action = .dismiss
    }
}

extension SheetManager.Action {
    var isPresented: Bool {
        guard case .present(_) = self else {return false}
        return true
    }
}

I have tried creating a typealias of Void and View so that popup can return that, and I have also tried making popup return just a closure so that I could call

.popup(with: sheetManager) { // action I want taken on the button of the popup view
} 

but I have met only build errors and no action. It took awhile to reduce the errors but there is still no action. the action I want to be done from the .popup is what I want propagated up from tappedProfile which is being called in the view modifier.

1

There are 1 best solutions below

0
kenada97 On
 func popup(with sheetManager: SheetManager, action: @escaping () -> Void) -> some View {
        self.modifier(PopupViewModifier(sheetManager: sheetManager){  action() })
    }

action() was never being used, and just needed to be called in the function