从SwiftUI按钮动作向UIView函数发送tapAction

时间:2019-06-13 15:38:05

标签: ios swift swiftui

我正在尝试找到一种方法来触发在feature_xyz中点击按钮时将在我的UIView中调用函数的操作。

  

这是我的设置:

swiftUI需要在foo()(UIView)被点击时运行

我使用AVFoundation框架的自定义UIView类

Button(SwiftUI)

要在swiftUI中使用我的UIView,必须将其包装在class SomeView: UIView { func foo() {} }

UIViewRepresentable

托管我的UIView()的SwiftUI视图

struct SomeViewRepresentable: UIViewRepresentable {

    func makeUIView(context: Context) -> CaptureView {
        SomeView()
    }

    func updateUIView(_ uiView: CaptureView, context: Context) {        
    }
}

3 个答案:

答案 0 :(得分:3)

您可以将自定义UIView的实例存储在可表示的结构(此处为SomeViewRepresentable)中,并在点击操作中调用其方法:

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()
  }
}

您的视图主体将如下所示:

  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")
        }
      }
    }
  }

要对其进行测试,我将打印品添加到foo()方法中:

class SomeView: UIView {

  func foo() {
    print("foo called!")
  }
}

现在点击按钮将触发foo()并显示打印语句。

答案 1 :(得分:0)

M Reza的解决方案适用于简单情况,但是,如果您的父SwiftUI视图具有状态更改,则每次刷新时,都会由于以下原因而导致UIViewRepresentable创建新的UIView实例:let someView = SomeView() // add this instance。因此,someView.foo()正在对您创建的SomeView的上一个实例调用操作,该实例在刷新时已过时,因此您可能看不到UIViewRepresentable的任何更新出现在父视图上。 参见:https://medium.com/zendesk-engineering/swiftui-uiview-a-simple-mistake-b794bd8c5678

更好的做法是避免在调用UIView的函数时创建和引用该实例。

我对M Reza解决方案的适应是通过父视图的状态更改间接调用该函数,该状态触发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
    }
  }
}

答案 2 :(得分:0)

这是使用桥接类的另一种方法。

//SwiftUI
struct SomeView: View{
  var bridge: BridgeStuff?

  var body: some View{
    Button("Click Me"){
      bridge?.yo()
    }
  }
}

//UIKit or AppKit (use NS instead of UI)
class BridgeStuff{
  var yo:() -> Void = {}
}

class YourViewController: UIViewController{

  override func viewDidLoad(){
    let bridge = BridgeStuff()
    let view = UIHostingController(rootView: SomeView(bridge: bridge))
    bridge.yo = { [weak self] in
      print("Yo")
      self?.howdy()
    }
  }

  func howdy(){
    print("Howdy")
  }
}