将 SwiftUI 按钮动作中的 tapAction 发送到 UIView 函数

8HP*_*HP8 10 ios swift swiftui

我试图找到一种方法来触发一个动作,UIView当一个按钮被点击时,它会在我的里面调用一个函数swiftUI

这是我的设置:

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

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

class SomeView: UIView {

    func foo() {}
}
Run Code Online (Sandbox Code Playgroud)

要在 swiftUI 中使用我的 UIView,我必须将它包裹起来 UIViewRepresentable

struct SomeViewRepresentable: UIViewRepresentable {

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

    func updateUIView(_ uiView: CaptureView, context: Context) {        
    }
}
Run Code Online (Sandbox Code Playgroud)

承载我的 UIView() 的 SwiftUI 视图

struct ContentView : View {

    var body: some View {
        VStack(alignment: .center, spacing: 24) {
            SomeViewRepresentable()
                .background(Color.gray)
            HStack {
                Button(action: {
                    print("SwiftUI: Button tapped")
                   // Call func in SomeView()
                }) {
                    Text("Tap Here")
                }
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

Mo *_*ani 13

您可以将自定义实例存储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()
  }
}
Run Code Online (Sandbox Code Playgroud)

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

  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")
        }
      }
    }
  }
Run Code Online (Sandbox Code Playgroud)

为了测试它,我在foo()方法中添加了一个打印:

class SomeView: UIView {

  func foo() {
    print("foo called!")
  }
}
Run Code Online (Sandbox Code Playgroud)

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

  • 保留对 uiview 的引用会给我一些奇怪的效果,因为它会重新绘制 uiview (3认同)
  • 它正在工作,但添加此实例变量会导致更新请求永远发送。当我删除它时,它不会被调用。我的 UIView 是 MKMapView,可能有所不同。 (2认同)

ada*_*086 8

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 的函数时避免创建和引用该 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
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 这会导致 -> [SwiftUI] 在视图更新期间修改状态,这将导致未定义的行为。这是因为您在更新块中再次将 buttonPressed 设置为 false。 (7认同)
  • 这应该是更正确的答案,因为这是在 SwiftUI 中执行此操作的正确方法......在 SwiftUI 中执行操作的思维方式与传统的 UIKit 有很大不同。 (2认同)