使用SwiftUI已经大半年了,对SwiftUI相关技术或多或少有一定地积累。很想把这些积累写成文章分享出来,也算对自己的一个输出。
而对于如何输入,最近我也一直在考虑。想来想去,就以一个系列的方式输出。对自己,能形成系统结构;对别人,也方便查找。
今天讲一讲不透明返回类型(Opaque Result Type)。
先看现象
在我们写SwiftUI的时候,经常看到下面的代码
ContentView使用了一个View返回类型。
body的类型是some View。
这两个是什么含义呢?
这内容就是我们这次要分享的不透明返回类型。
分析
首先需要明白,这里View,非OC中的那个UIView,也非之前了解的视图的概念。在这里,它是一个新的概念--叫协议。
View是SwiftUI中一个最核心的协议,它表示屏幕上元素的描述,并且该协议含有一个associatedtype类型。
View协议的源码如下。
public protocol View : _View {
associatedtype Body : View
var body: Self.Body { get }
}
这里申明了一个associatedtype类型的View。
associatedtype类型是什么概念呢?它是关联类型,类似于泛型(T)。作用是在传递参数或者返回值,不用固定某类型,而是可以比较宽泛的类型。相似于Any类型。
View协议在associatedtype基础上还有类型约束的作用。简单来说,它控制或者约束了参数或返回值的类型,不能随便输入或者不输入,需要遵循协议要求。
这也说明了它不是一个数据类型了,不能做类型来使用了。
如下用代码说明:
func createView(T:View)() -> T {
}
这种方式是正确的,带泛型来约束返回值类型。不是做返回类型。
func ceateView() -> View {
}
这种方式是错误的,指定了类型。
解释完了View的含义,再看看body返回some View是什么含义呢?它有什么好处呢?
首先body是SwiftUI界面的主体,类似一块画布。视图的控件都在这“画布”上挂载并显示。
然后就是返回some view的问题,这个答案就要回归SwiftUI的使用场景了,我们都知道SwiftUI包含一套UI控件工具类。它里面有很多的控件,如Text,Button,Image等等。我们在界面上都是使用这些控件组合成我们需要的界面。
我们先假设界面上就一个Text控件。
按Xcode项目,帮我们生成的代码如下:
struct ContentView : View {
var body: some View {
Text("Hello World")
}
}
其实等价于如下的代码
struct ContentView : View {
var body: Text {
Text("Hello World")
}
}
本质上, some View就是Text控件。
当界面上有很多控件的时候。例如,在登录界面,我们有一个logo图片,登录名标签,登录名输入框,密码标签,密码输入框和登录按钮。
是按下图的方式实现吗?
若按上图实现,是有问题的,返回了多个控件。返回的是Image,Text还是Button控件,不能明确。
按照SwiftUI的设计,应该统一返回一个类型。那怎么返回一个类型呢?这里就是加个VStack
struct ContentView: View {
@State var userName = ""
@State var password = ""
var body: some View {
VStack {
Image("logo.png")
Text("用户名")
TextField("UserName", text: $userName)
Text("密码")
TextField("password",text:$password)
Button {
print("")
} label: {
Text("登录按钮")
}
}
}
}
统一封装到VStack中,最后返回类型是VStack了。虽然VStack里面有很多不同的控件,但最终是打包成VStack并以VStack返回。
在上述两个例子中,some view有时候是Text,有时候是VStack。这就是some的不透明性。
所有使用some View 来修饰,有以下好处:
1.每次返回的一定是一个确定、且遵守View协议的类型;
2.并不关心具体返回哪种类型。
这样的设计,为开发者提供了一个灵活的开发模式,抹掉了具体的类型,不需要修改公共API来确定每次闭包的返回类型。也降低了代码书写难度。
总结
Swft5.1版本新增加了 Opaque Result Type(不透明返回类型),更加方便了SwiftUI开发。
而不透明返回类型,本质上是,在SwiftUI开发过程中,很多控件组合一起显示界面时,每次统一返回的类型是一个确定的、且遵守View协议的类型,使开发者不用去关心到底是哪个控件的类型,反正是确定类型且遵守View协议。这样大大提高了开发效率。