SwiftUI = UIViewControllerRepresentable通过@Binding

时间:2019-11-26 15:43:26

标签: swift swiftui

从SwiftUI,我试图使用按钮在UIViewControllerRepresentable中实现文本更改,但可能缺少有关绑定的关键概念。

在SwiftUI中:

struct MainView: View {
 @State private var textHere = "Set up text"
  var body: some View {
   return VStack{
    DisplayView(text: $textHere)
    Button(action:{
     self.textHere = "Button update text" 
    }) {
     HStack {
      Text("Change the text by clicking")
      .background(Color.white)
     }
    }
    .background(Color.blue)
   }
  }
}

然后我有一个UIViewControllerRepresentable:

struct DisplayView: UIViewControllerRepresentable  {
 @Binding var text: String

  func makeUIViewController(context: UIViewControllerRepresentableContext< DisplayView >) -> UIViewController {
   let controller = DisplayViewController()
   controller.text = text
   return controller
  }

  func updateUIViewController(_ uiViewController: UIViewController, context: UIViewControllerRepresentableContext< DisplayView >) {
   let controller = DisplayViewController()
   controller.text = text
   controller.updateText()
  }
}

我的ViewController:

class CameraViewController : UIViewController {
 var text = "Hello"
 var textViewTest = UILabel()

 func updateText(){
  print("update text " + text)
  self.textViewTest = UILabel(frame: CGRect(x: 40.0, y: 120.0, width: 250.0, height: 100.0))
  ...
  self.textViewTest.text = text
 }

 override func viewDidLoad() {
  super.viewDidLoad()
  self.textViewTest = UILabel(frame: CGRect(x: 40.0, y: 120.0, width: 250.0, height: 100.0))
  ...
  self.textViewTest.text = text
  view.addSubview(self.textViewTest)
 }
}

我已经尝试了updateText()函数的一些变体。

通过单击按钮传递数据:

  • “按钮更新文本”被传递到UIViewControllerRepresentable的updateUIViewController方法中
  • “按钮更新文本”被传递到的updateText()函数中。 UIViewController
  • “按钮更新文本”被传递到UIViewController的viewDidLoad中。

但是值传递不会在标签上显示为更改。

似乎是在ViewController中进行了更新,但SwiftUI视图没有更新?

编辑:

This article向我指出了“包装” UIRepresentable的方向。这是新版本:

    struct DisplayView: UIViewControllerRepresentable  {
     typealias UIViewControllerType = CameraViewController
     var text: String?

     func makeUIViewController(context: UIViewControllerRepresentableContext< DisplayView >) -> CameraViewController {
      print("view called")
      return CameraViewController()
     }

     func updateUIViewController(_ cameraViewController: CameraViewController, context: UIViewControllerRepresentableContext< DisplayView >) {
      print("View updated")
      cameraViewController.text = text
     }
    }

ViewController已更新:

final class CameraViewController : UIViewController {
 var text: String? {
  didSet {
   updateController()
  }
 }

 private var ccmeraViewController: CameraViewController?

 func updateController(){
  let textViewTest = UILabel(frame: CGRect(x: 40.0, y: 120.0, width: 250.0, height: 100.0))
  guard let text = text else { return }

  textViewTest.center = self.view.center
  textViewTest.textAlignment = NSTextAlignment.justified
  textViewTest.textColor = UIColor.blue
  textViewTest.backgroundColor = UIColor.lightGray
  textViewTest.text = text
  view.addSubview(textViewTest)
 }

 override func viewDidLoad() {
  super.viewDidLoad()
  //updateController()
 }

太好了!现在,我们可以正确地从SwiftUI将String值传递给ViewController并更新标签。

但是,当您第一次打开页面时,都会同时调用makeUIViewController和updateUIViewController,因此标签的两个实例相互重叠。

所以最后一步是弄清楚如何只显示一次UILabel。

更新3和部分解决方案

包含解决方案can be found here的最小存储库。

答案是正确的,您需要添加@Binding。但是,有趣的是,将字符串绑定到显示标签的ViewController会导致在视图中出现标签的两个实例。

通过将UILabel从SwiftUI绑定到UIViewControllerRepresentable,可以解决此问题,但是当String值更新时,标签会更改位置。

我不清楚:a)为什么UIViewControllerRepresentable接收到两个更新视图的请求,或者b)为什么UILabel更改位置。

同样,从SwiftUI绑定UILabel也没有多大意义。这本来是对嵌入ViewController并使用它们显示标签的测试,但这并不是您真正想要做的,但是为什么发生这些问题却没有多大意义。

1 个答案:

答案 0 :(得分:1)

您可能需要像这样绑定text

 struct DisplayView: UIViewControllerRepresentable  {
 typealias UIViewControllerType = CameraViewController
  @Binding var text: String
 ....