SwiftUI中UIViewControllerRepresentable的使用(UIKit与SwiftUI的桥梁)

UIViewControllerRepresentable是一个协议,用于创建一个SwiftUI视图,该视图包装了一个UIKit中的UIViewController。通过实现UIViewControllerRepresentable协议,我们可以在SwiftUI中使用自定义的UIViewController,并与SwiftUI的生命周期进行交互。

实现UIViewControllerRepresentable

创建一个遵循UIViewControllerRepresentable协议的自定义结构体,并实现下面两个基础的方法:

  • makeUIViewController(context:):创建并返回UIViewController
  • updateUIViewController(_:context:):更新UIViewController

这里创建一个自定义结构体MyViewControllerRepresentable,并继承UIViewControllerRepresentable协议。该结构体里面包装一个MyViewController

struct MyViewControllerRepresentable: UIViewControllerRepresentable {

  func makeUIViewController(context: Context) -> MyViewController {
    let vc = MyViewController()
    return vc
  }

  func updateUIViewController(_ uiViewController: MyViewController, context: Context) {

  }
}

class MyViewController: UIViewController {

  override func viewDidLoad() {
    super.viewDidLoad()
    view.backgroundColor = UIColor.blue
  }
}

调用代码如下:

struct UIViewControllerRepresentableDemo: View {
  @State private var isShow: Bool = false
  var body: some View {
    Button(action: {
      isShow = true
    }, label: {
      Text("Enter next page")
        .padding()
        .foregroundColor(.white)
        .background(Color.blue)
        .clipShape(Capsule())
    })
    .sheet(isPresented: $isShow) {
      MyViewControllerRepresentable()
    }
  }
}

在这里插入图片描述

SwiftUI向UIKit传值

SwiftUI跳转到下一个界面的时候,我们经常会传入一些数据过去,不像UIViewRepresentable协议在UI刷新的时候调用updateUIView传递参数。在这里下一个界面不会根据上一个界面的状态去刷新的,更多的是一次性传值,既然是这样,那在初始化的时候直接把数值传过来。updateUIViewController方法基本上就用不上了。

比如要在MyViewController中显示一段文字,修改MyViewController如下:

class MyViewController: UIViewController {
  var text: String = ""

  override func viewDidLoad() {
    super.viewDidLoad()
    view.backgroundColor = UIColor.blue

    var label = UILabel()
    label.font = .systemFont(ofSize: 30)
    label.text = text
    label.textColor = .white
    label.textAlignment = .center
    label.frame = view.bounds
    view.addSubview(label)
  }
}

那么在MyViewControllerRepresentable创建的时候需要从SwiftUI界面接收数值,然后再传递给MyViewController

struct MyViewControllerRepresentable: UIViewControllerRepresentable {
  let text: String

  func makeUIViewController(context: Context) -> MyViewController {
    let vc = MyViewController()
    vc.text = text
    return vc
  }

  func updateUIViewController(_ uiViewController: MyViewController, context: Context) {

  }
}

完成上面两步后在SwiftUI界面初始化MyViewControllerRepresentable的时候直接传入参数。

struct UIViewControllerRepresentableDemo: View {
  @State private var isShow: Bool = false
  var body: some View {
    Button(action: {
      isShow = true
    }, label: {
      Text("Enter next page")
        .padding()
        .foregroundColor(.white)
        .background(Color.blue)
        .clipShape(Capsule())
    })
    .sheet(isPresented: $isShow) {
      MyViewControllerRepresentable(text: "This is a text")
    }
  }
}

在调用MyViewControllerRepresentable的时候,直接传入参数,效果如下:

在这里插入图片描述

UIKit向SwiftUI传值

UIKit中,我们从第二个界面想第一个界面传值一般通过代理或者block的方式回传,下面这个示例采用block方式将数值从UIKit传到SwiftUI

class MyViewController: UIViewController, UITextFieldDelegate {
  var text: String = ""

  var editResult: ((String?) -> Void)?

  override func viewDidLoad() {
    super.viewDidLoad()
    view.backgroundColor = UIColor.blue

    let label = UILabel()
    label.font = .systemFont(ofSize: 30)
    label.text = text
    label.textColor = .white
    label.textAlignment = .center
    label.frame = CGRect(x: 10, y: 100, width: UIScreen.main.bounds.width - 20, height: 50)
    view.addSubview(label)

    let textField = UITextField()
    textField.borderStyle = .roundedRect
    textField.frame = CGRect(x: 10, y: 200, width: UIScreen.main.bounds.width - 20, height: 50)
    view.addSubview(textField)
    textField.delegate = self
  }

  func textFieldDidChangeSelection(_ textField: UITextField) {
    editResult?(textField.text)
  }
}

首先修改MyViewController中的代码,添加一个TextField,输入文字后在SwiftUI界面显示出来。这里定义了一个var editResult: ((String?) -> Void)?属性,在UITextFieldDelegate的方法中调用传值,具体见上面代码。

修改MyViewControllerRepresentable如下:

struct MyViewControllerRepresentable: UIViewControllerRepresentable {
  let text: String
  @Binding var editText: String?

  func makeUIViewController(context: Context) -> MyViewController {
    let vc = MyViewController()
    vc.text = text
    vc.editResult = { text in
      editText = text
    }
    return vc
  }
  func updateUIViewController(_ uiViewController: MyViewController, context: Context) {

  }
}

MyViewControllerRepresentable结构体中,定义一个@Binding修饰的editText属性,用来绑定SwiftUI界面的属性,并且记录MyViewController传回来的值。

SwiftUI界面调用部分:

struct UIViewControllerRepresentableDemo: View {
  @State private var isShow: Bool = false
  @State var editText: String?

  var body: some View {
    VStack {
      if let editText {
        Text(editText)
          .padding()
      }

      Button(action: {
        isShow = true
      }, label: {
        Text("Enter next page")
          .padding()
          .foregroundColor(.white)
          .background(Color.blue)
          .clipShape(Capsule())
      })
      .sheet(isPresented: $isShow) {
        MyViewControllerRepresentable(text: "This is a text", editText: $editText)
    }
    }
  }
}

效果如下:
在这里插入图片描述

上面的反向传值太过简单了,都没有用上Coordinate,在上一篇文章中,我们用CoordinateUITextField的输入内容传到了SwiftUI界面。下面看一下在UIViewControllerRepresentable中采用CoordinateSwiftUI传值,这个和上一篇文章中的传值方法一样,这里就不过多阐述了,直接上完整代码。如果对具体步骤感兴趣的朋友可以看一下上一篇文章

struct UIViewControllerRepresentableDemo1: View {
  @State private var isShow: Bool = false
  @State var image: UIImage?

  var body: some View {
    VStack {
      if let image {
        Image(uiImage: image)
          .resizable()
          .aspectRatio(contentMode: .fill)
          .frame(width: 300, height: 200)
          .clipped()
      }

      Button(action: {
        isShow = true
      }, label: {
        Text("Show image picker")
          .padding()
          .foregroundColor(.white)
          .background(Color.blue)
          .clipShape(Capsule())
      })
      .sheet(isPresented: $isShow) {
        ImagePickerViewControllerRepresentable(isShow: $isShow, image: $image)
    }
    }
  }
}

struct ImagePickerViewControllerRepresentable: UIViewControllerRepresentable {
  @Binding var isShow: Bool
  @Binding var image: UIImage?

  func makeUIViewController(context: Context) -> UIImagePickerController {
    let imagePicker = UIImagePickerController()
    imagePicker.allowsEditing = false
    imagePicker.delegate = context.coordinator
    return imagePicker
  }

  func updateUIViewController(_ uiViewController: UIImagePickerController, context: Context) {

  }

  func makeCoordinator() -> Coordinator {
    Coordinator(isShow: $isShow, image: $image)
  }

  class Coordinator: NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate {

    @Binding var isShow: Bool
    @Binding var image: UIImage?

    init(isShow: Binding<Bool>, image: Binding<UIImage?>) {
      self._isShow = isShow
      self._image = image
    }

    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
      guard let newImage = info[.originalImage] as? UIImage else { return }
      image = newImage
      isShow = false
    }

  }
}

上面代码中点击后弹出一个ImagePicker界面,选择图片后传给SwiftUI界面显示。
在这里插入图片描述

写在最后

通过UIViewControllerRepresentable,我们可以在SwiftUI应用程序中轻松地集成UIKit中的UIViewController,实现更加灵活和强大的界面。掌握UIViewControllerRepresentable的使用方法可以让我们更好地利用SwiftUIUIKit的优势,为应用程序提供更好的用户体验。

最后,希望能够帮助到有需要的朋友,如果觉得有帮助,还望点个赞,添加个关注,笔者也会不断地努力,写出更多更好用的文章。

  • 29
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值