Send tapAction from SwiftUI button action to UIView function

后端 未结 2 1540
情歌与酒
情歌与酒 2020-12-20 14:38

I\'m trying to find a way to trigger an action that will call a function in my UIView when a button gets tapped inside swiftUI.

相关标签:
2条回答
  • 2020-12-20 15:27

    You can store an instance of your custom UIView in your representable struct (SomeViewRepresentable here) and call its methods on tap actions:

    struct SomeViewRepresentable: UIViewRepresentable {
    
      let someView = SomeView() // add this instance
    
      func makeUIView(context: Context) -> SomeView { // changed your CaptureView to SomeView to make it compile
        someView
      }
    
      func updateUIView(_ uiView: SomeView, context: Context) {
    
      }
    
      func callFoo() {
        someView.foo()
      }
    }
    

    And your view body will look like this:

      let someView = SomeViewRepresentable()
    
      var body: some View {
        VStack(alignment: .center, spacing: 24) {
          someView
            .background(Color.gray)
          HStack {
            Button(action: {
              print("SwiftUI: Button tapped")
              // Call func in SomeView()
              self.someView.callFoo()
            }) {
              Text("Tap Here")
            }
          }
        }
      }
    

    To test it I added a print to the foo() method:

    class SomeView: UIView {
    
      func foo() {
        print("foo called!")
      }
    }
    

    Now tapping on your button will trigger foo() and the print statement will be shown.

    0 讨论(0)
  • 2020-12-20 15:28

    M Reza's solution works for simple situations, however if your parent SwiftUI view has state changes, every time when it refreshes, it will cause your UIViewRepresentable to create new instance of UIView because of this: let someView = SomeView() // add this instance. Therefore someView.foo() is calling the action on the previous instance of SomeView you created, which is already outdated upon refreshing, so you might not see any updates of your UIViewRepresentable appear on your parent view. See: https://medium.com/zendesk-engineering/swiftui-uiview-a-simple-mistake-b794bd8c5678

    A better practice would be to avoid creating and referencing that instance of UIView when calling its function.

    My adaption to M Reza's solution would be calling the function indirectly through parent view's state change, which triggers updateUIView :

      var body: some View {
        @State var buttonPressed: Bool = false
        VStack(alignment: .center, spacing: 24) {
    
          //pass in the @State variable which triggers actions in updateUIVIew
          SomeViewRepresentable(buttonPressed: $buttonPressed)
            .background(Color.gray)
          HStack {
            Button(action: {
              buttonPressed = true
            }) {
              Text("Tap Here")
            }
          }
        }
      }
    
    struct SomeViewRepresentable: UIViewRepresentable {
      @Binding var buttonPressed: Bool 
    
      func makeUIView(context: Context) -> SomeView {
        return SomeView()
      }
    
      //called every time buttonPressed is updated
      func updateUIView(_ uiView: SomeView, context: Context) {
        if buttonPressed {
            //called on that instance of SomeView that you see in the parent view
            uiView.foo()
            buttonPressed = false
        }
      }
    }
    
    0 讨论(0)
提交回复
热议问题