【问题标题】:Send tapAction from SwiftUI button action to UIView function将 SwiftUI 按钮操作中的 tapAction 发送到 UIView 函数
【发布时间】:2019-06-13 15:38:05
【问题描述】:

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

这是我的设置:

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

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

class SomeView: UIView {

    func foo() {}
}

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

struct SomeViewRepresentable: UIViewRepresentable {

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

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

托管我的 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")
                }
            }
        }
    }
}

【问题讨论】:

标签: ios swift swiftui


【解决方案1】:

您可以将自定义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() 并显示打印语句。

【讨论】:

  • 不敢相信它这么简单。不知道为什么我认为它会有所不同。非常感谢?
  • 该死!我今天一直在尝试这样做——从SwiftUI 推送或触发UIKit 中的某些内容,实际上几分钟前创建了一个问题。现在已删除。我只是希望我能给你一个以上的支持。
  • 哈哈就这么简单吧? ?
  • 保留对 uiview 的引用会给我一些奇怪的效果,因为它会重绘 uiview
  • 它正在工作,但添加此实例变量会导致更新请求永远被发送。当我删除它时,它不会被调用。我的 UIView 是 MKMapView,可能会有所不同。
【解决方案2】:

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

【讨论】:

  • 这应该是更正确的答案,因为这是在 SwiftUI 中的正确做法……在 SwiftUI 中做事的心态与传统的 UIKit 有很大不同。
  • 这会导致 -> [SwiftUI] 在视图更新期间修改状态,这将导致未定义的行为。这是因为您在更新块中再次将 buttonPressed 设置为 false。
【解决方案3】:

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-09-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多