Hacking with iOS: SwiftUI Edition

在SwiftUI视图中包装UIViewController

2020-06-19  本文已影响0人  韦弦Zhy

\color{red}{\Large \mathbf{Hacking \quad with \quad iOS: SwiftUI \quad Edition}}

{\Large \mathbf{Instafilter}}

SwiftUI是构建应用程序的绝佳框架,但目前还远远不够完善——它有很多事情做不到,因此,如果您想添加更多高级功能,则需要学习与UIKit交流。有时,这是为了整合您为UIKit编写的现有代码(例如,如果您为拥有现有UIKit应用程序的公司工作),但有时是因为UIKit和Apple的其他框架向我们提供了我们想要展示在SwiftUI布局中的有用代码。

在此项目中,我们将要求用户从其照片库中导入图片。UIKit随附了专用的代码来执行此操作,但尚未移植到SwiftUI,因此我们需要自己编写桥接。

在编写代码之前,您需要了解三件事,所有这些都有点像UIKit 101,但是如果您仅使用SwiftUI,它们对您来说则是全新的东西。

首先,UIKit有一个名为UIView的类,它是布局中所有视图的父类。因此,标签,按钮,文本字段,滑块等等——都是视图。

其次,UIKit有一个名为UIViewController的类,该类旨在保存所有代码以使视图栩栩如生。就像UIView一样,UIViewController具有许多子类,它们可以完成各种工作。

第三,UIKit使用一种称为委托的设计模式来确定工作在哪里进行。因此,在决定我们的代码如何响应文本字段的值更改时,我们将使用我们的功能创建一个自定义类,并让我们成为文本字段的委托。

SwiftUI使我们的代码结构非常不同,不仅如此,我们主要将结构体用于视图而非类。但是,SwiftUI将UIViewUIViewController混合到单个View协议中,这使我们的代码更加简单。

无论如何,所有这些都很重要,因为UIKit要求用户从其图库中选择照片的系统使用名为UIImagePickerController的视图控制器以及名为UINavigationControllerDelegateUIImagePickerControllerDelegate的委托协议。SwiftUI无法直接使用这两个,因此我们需要对其进行包装。

我们将从简单开始,逐步发展。包装UIKit视图控制器要求我们创建一个符合UIViewControllerRepresentable协议的结构体。这是基于View的,这意味着我们定义的结构可以在SwiftUI视图层次结构体内使用,但是我们没有提供body属性,因为视图的主体是视图控制器本身——它仅显示UIKit传回的内容。

符合UIViewControllerRepresentable确实需要我们实现两种方法:一种名为makeUIViewController(),用于创建初始视图控制器,另一种名为updateUIViewController(),其设计用于在某些SwiftUI状态更改时让我们更新视图控制器。

按Cmd + N新建一个文件,选择“Swift File”,并将其命名为ImagePicker.swift。将import SwiftUI添加到新文件的顶部,然后为其提供以下代码:

struct ImagePicker: UIViewControllerRepresentable {
    typealias UIViewControllerType = UIImagePickerController
}

这还不足以正确编译代码,但是当Xcode显示错误消息“Type ImagePicker does not conform to protocol UIViewControllerRepresentable”时,请点击错误左侧的红色和白色圆圈,然后选择“fix”。这将使Xcode编写我们实际需要的两个方法,实际上,这些方法实际上足以让Swift找出视图控制器的类型,因此您可以删除typealias行。

您应该具有以下结构体:

struct ImagePicker: UIViewControllerRepresentable {
    func makeUIViewController(context: UIViewControllerRepresentableContext<ImagePicker>) -> UIImagePickerController {
        code
    }

    func updateUIViewController(_ uiViewController: UIImagePickerController, context: UIViewControllerRepresentableContext<ImagePicker>) {
        code
    }
}

我们将不会使用updateUIViewController(),因此您只需删除代码(“code”)行,以使该方法为空,然后在第一个方法编写如下内容:

let picker = UIImagePickerController()
return picker

不久我们将向其中添加更多代码,但这实际上是包装基本视图控制器所需的全部。

我们的ImagePicker结构体是有效的SwiftUI视图,这意味着我们现在可以像其他任何SwiftUI视图一样在工作表中显示它。这个特定的结构体旨在显示图像,因此我们需要一个可选的Image视图来保存所选图像,以及确定该表是否在显示的某种状态。

将此替换为当前的ContentView结构体:

struct ContentView: View {
    @State private var image: Image?
    @State private var showingImagePicker = false

    var body: some View {
        VStack {
            image?
                .resizable()
                .scaledToFit()

            Button("Select Image") {
               self.showingImagePicker = true
            }
        }
        .sheet(isPresented: $showingImagePicker) {
            ImagePicker()
        }
    }
}

继续并在模拟器或真实设备上运行它。当您点击按钮时,默认的UIKit图像选择器应向上滑动,以浏览所有照片,而当您选择其中一张时,它将消失。

但是,尽管我们刚刚选择了一张图像,但在我们的视图中不会出现任何图像。您会看到,即使我们在视图中放置了SwiftUI图像,也没有将UIImagePickerController中的选择分配给它的任何地方。

为了实现这一目标,需要一个全新的概念:coordinators。

Wrapping a UIViewController in a SwiftUI view

上一篇下一篇

猜你喜欢

热点阅读