SwiftUI最强大的特征之一就是它的视图默认是可组合的。View 正因为如此,拆分一个庞大的视图往往只需要将其body ,然后根据需要将任何绑定或其他类型的动作接线。
然而,当我们想拆分一个大的视图以提高其可维护性和可读性时,也有许多其他的技术可以被记住。为了看一看这样的技术,让我们从下面这个ProfileView ,它目前使用一个单一的body 属性来形成其内部的视图层次。
struct ProfileView: View {
var user: User
var body: some View {
ZStack {
LinearGradient(
gradient: user.profileGradient,
startPoint: .top,
endPoint: .bottom
)
.edgesIgnoringSafeArea(.all)
ScrollView {
VStack(spacing: 15) {
Text(user.name)
.font(.title)
Text(user.biography)
.multilineTextAlignment(.leading)
}
.padding()
.foregroundColor(.white)
}
}
}
}
虽然从行数上看,上面的视图并不庞大,但它绝对可以被拆分,以便随着时间的推移,更容易迭代和扩展其功能。要做到这一点的一个方法是使用前面提到的方法,将其body 属性分割成独立的View 类型--在这种情况下可以实现为私有嵌套类型,以使我们的代码具有整齐的命名间距。
private extension ProfileView {
struct BackgroundView: View {
var gradient: Gradient
var body: some View {
LinearGradient(
gradient: gradient,
startPoint: .top,
endPoint: .bottom
)
.edgesIgnoringSafeArea(.all)
}
}
struct InfoView: View {
var user: User
var body: some View {
VStack(spacing: 15) {
Text(user.name)
.font(.title)
Text(user.biography)
.multilineTextAlignment(.leading)
}
.padding()
.foregroundColor(.white)
}
}
}
有了上述方法,我们现在可以大大简化我们的主ProfileView ,因为我们现在可以通过在各自的ZStack 和ScrollView 包装器中实例化我们的BackgroundView 和InfoView 来实现其body 。
struct ProfileView: View {
var user: User
var body: some View {
ZStack {
BackgroundView(gradient: user.profileGradient)
ScrollView {
InfoView(user: user)
}
}
}
}
然而,虽然上述方法在很多情况下绝对是一个很好的选择,但在这种情况下,它感觉有点 "沉重"--因为我们既需要定义多个类型来分割我们的代码,又需要将我们的部分模型数据传递到这些嵌套的视图中,这很快就会让人觉得是不必要的忙碌工作。
因此,让我们探索另一种方法,这涉及到为我们视图内部层次结构的不同部分创建额外的body--类似于计算的属性。因为在这种情况下,我们的视图的前景和背景之间有一个非常自然的分离,让我们给我们的属性命名,如果我们使用与SwiftUI的内置body 属性相同的some View 不透明的返回类型,那么我们最终会得到以下的实现。
private extension ProfileView {
var background: some View {
LinearGradient(
gradient: user.profileGradient,
startPoint: .top,
endPoint: .bottom
)
.edgesIgnoringSafeArea(.all)
}
var foreground: some View {
ScrollView {
VStack(spacing: 15) {
Text(user.name)
.font(.title)
Text(user.biography)
.multilineTextAlignment(.leading)
}
.padding()
.foregroundColor(.white)
}
}
}
有了这个改变,我们现在要做的就是用一个ZStack ,把我们的background 和foreground 结合起来--这就给了我们一个body 的实现,它基本上是我们的私有实现和SwiftUI这个框架之间的一个 "交接点"。
struct ProfileView: View {
var user: User
var body: some View {
ZStack {
background
foreground
}
}
}
上述方法的好处是,它不需要我们传递任何额外的数据,因为我们的整个实现现在实际上可以保持在一个单一的类型中--同时仍然实现了我们视图的各个部分之间非常好的分离。
当然,将某些视图分割成完全独立的类型仍然是一个好主意(特别是如果我们想让其中一些部分更容易被重用),但是当我们想分割一个更大的视图,同时让它的实现继续作为一个单元时,上述技术可以很好地被记住。