3

I have custom ActionView with two buttons: Car and Bike. When these buttons tapped I need that in the MainView modifiers onCarTap/onBikeTap will be triggered.

With my current implementation here is error:

  • Argument passed to call that takes no arguments
  • Value of tuple type 'Void' has no member 'onBikeTap'

Source code:

struct ActionView: View {
    // Callback for button taps
    var onCarTap: (() -> Void)?
    var onBikeTap: (() -> Void)?
    
    var body: some View {
        HStack {
            Button(action: {
                onCarTap?()
            }, label: {
                Text("Car")
            })
            Button(action: {
                onBikeTap?()
            }, label: {
                Text("Bike")
            })
        }
    }
}

I am looking for solution like this:

struct MainView: View {
    var body: some View {
        ActionView()
            .onCarTap({})
            .onBikeTap({ })
    }
}

It is possible to implement in this way:

    ActionView(onCarTap: {
        print("on car tap")
    }, onBikeTap: {
        print("on bike tap")
    })

2 Answers 2

6

You can declare a modifier for your purpose like the following.

extension ActionView {
    func onCarTap(_ handler: @escaping () -> Void) -> ActionView {
        var new = self
        new.onCarTap = handler
        return new
    }
}

In addition, if you prefer to hide the handler property with private or fileprivate to prevent it from being accessed directly, you have to declare a designated init which accepts parameters for its properties except one for the handler.

Sign up to request clarification or add additional context in comments.

2 Comments

"except one for the handler": this was the final hint I needed, as it is impossible to override the handler a second time, even if it is a State wrapped property. Thank you!
Use an optional closure to avoid the @escaping
6

Assuming you have the following View:

struct ActionView: View {
    var onCarTapAction: (() -> Void)?
    var onBikeTapAction: (() -> Void)?

    var body: some View {
        HStack {
            Button(action: {
                onCarTapAction?()
            }, label: {
                Text("Car")
            })
            Button(action: {
                onBikeTapAction?()
            }, label: {
                Text("Bike")
            })
        }
    }
}

You can create an extension:

extension ActionView {
    func onCarTap(action: @escaping (() -> Void)) -> ActionView {
        ActionView(onCarTapAction: action, onBikeTapAction: onBikeTapAction)
    }

    func onBikeTap(action: @escaping (() -> Void)) -> ActionView {
        ActionView(onCarTapAction: onCarTapAction, onBikeTapAction: action)
    }
}

and use it like this:

struct ContentView: View {
    var body: some View {
        ActionView()
            .onCarTap {
                print("onCarTap")
            }
            .onBikeTap {
                print("onBikeTap")
            }
    }
}

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.