eivindml
eivindml

Reputation: 2529

Add a onChanged callback to a SwiftUI struct for emitting values

I'm trying to emmit values from a SwiftUI component. I wan't to have an API like DragGesture (as an example).

DragGesture()
  .onChanged { value in
     print("Get access to value when it changes \(value)")
  }

How can I add a callback function like that to MyCustomComponent? I would prefer to have the exact same syntax. The component will emit values continously (based on a DragGesture locally inside the component. Code for the Component I wan't to augment can be seen here.

Upvotes: 2

Views: 2635

Answers (2)

eivindml
eivindml

Reputation: 2529

Found this answer by @Asperi, which shows how to do it like I wanted. The trick is to actually return a copy of itself.

typealias OnChange = ((CGFloat) -> Void)?

struct WheelView: View {
  var action: OnChange

  func onChanged(perform action: OnChange) -> Self {
    var copy = self
    copy.action = action
    return copy
  }

  var body: some View {
    Circle()
      .gesture(DragGesture()
        .onChanged { value in
          // 📣 Emit the angle change
          if let action = self.action {
            action(0.4)
          }
      })
  }
}

Then we can use our component like this:

struct Usage: View {
  var body: some View {
    WheelView()
      .onChanged { value in
        print("Value is \(value)")
    }
  }
}

Upvotes: 3

Asperi
Asperi

Reputation: 258355

Here is one of possible approaches (simplest IMO) ...

struct WheelView: View {
    var onEmit: (CGFloat) -> Void

    init(onEmit: @escaping (CGFloat)-> Void = {_ in }) {
        self.onEmit = onEmit
    }

    ...

    // TODO: Emit angle from here
    self.onEmit(self.angle)
}

usage (in any place of ViewBuilder)

WheelView() { angle in
    print("\(angle)")
}

Upvotes: 3

Related Questions