概览
在 SwiftUI 的世界里,我们无数次都梦想着视图可以自动根据布局上下文“因势而变”。大多数情况下,SwiftUI 会将每个视图尺寸处理的井井有条,不过在某些时候我们还是得亲力亲为。
如上图所示,无论顶部 TabView 容器里子视图高度如何变化,TabView 本身的高度都能“随遇而安”。如何用最简单、最现代化、最有趣且最切中要害的方法让容器尺寸与子视图的高度“如影随形”呢?
在本篇博文中,您将学到如下内容:
相信学完本课后,小伙伴们必能脑洞大开、格局打开,用“千姿百态”的方法让问题的解决一发入魂、九转功成!
那还等什么呢?Let‘s go!!!😉
1. 为什么要让视图高度自适应?
对于一个具体的示例来说,设想这样一种场景:每个 SwiftUI 子视图都包含了长度不定的文本,如何稳妥而优雅的据此设定它们父容器的高度呢?
在下面的代码中,我们的父容器 TabView 包含了若干个 likeIdiomCard 视图,每个 likeIdiomCard 视图对应一条自己喜爱的成语,其中每条成语又含有不固定长度的文本来表示自身的释义:
TabView {
ForEach(likeIdioms.chunked(into: 2), id: \.self) { idiomChunk in
HStack {
ForEach(idiomChunk) { idiom in
likeIdiomCard(idiom)
}
if idiomChunk.count == 1 {
Rectangle()
.foregroundStyle(.clear)
}
}
}
}
.tabViewStyle(.page)
.frame(height: 200)
注意,为了简便起见我们将整个 TabView 的高度设置为了 200。不过,这样做的缺点也显而易见:
- 当成语释义太短时,固定高度造成空间浪费;
- 当成语释义太长时,固定高度又会造成空间不足;
如下图所示,左侧的成语释义很短,会导致底部残留过多空间,而右侧释义很长的成语会迫使 likeIdiomCard 在有限的空间里居中显示,造成顶部成语名称显示不全。
在这个接地气的例子中,如果 TabView 的高度可以根据每个 likeIdiomCard 视图的高度自动调整那就天衣无缝了。
当然,我们完全可以用自定义布局(Layout)来让挑战大功告成,可是这颇有些“导弹打蚊子”的感觉。不过为了确保整个讨论的完整性,我们仍然会在本系列最后一篇文章介绍如何用自定义布局完成 SwiftUI 视图高度自适应的第 6 种实现。
下面,就让我们先选出 5 种稍显“轻量级”的方法来让问题迎刃而解吧。
2. 初始工作
首先,为了保存所有 likeIdiomCard 子视图的最大高度,我们需要在主视图中创建一个 maxHeight 状态:
@State private var maxHeight = 0.0
我们的基本思路是,依次获取每个 likeIdiomCard 视图的高度,并始终将最大的那个保存到 maxHeight 里。
最后,我们只需设置 TabView 的高度为 maxHeight 即可:
TabView {
//...
}
.tabViewStyle(.page)
.frame(height: maxHeight)
3. 最古老的方法:GeometryReader
早在 SwiftUI(iOS 13)诞生那天,GeometryReader 视图就作为一个重要成员与之携手并肩了。
如果我没记错,GeometryReader 至今已经快 6 年了。虽然有许多不足之处,但它们并不影响我们使用 GeometryReader 来得偿所愿:
likeIdiomCard(idiom)
.background {
GeometryReader { proxy in
if proxy.size.height > maxHeight {
maxHeight = proxy.size.height
}
return Color.clear
}
}
在上面的代码中,我们机智的将 GeometryReader 作为 likeIdiomCard 的背景,这样做避免了它们的尺寸“各持己见”。利用 @ViewBuilder 语法的强大威力,我们将 maxHeight 状态的设置逻辑与返回“占位”视图完美的“融为一体”。
更多关于 GeometryReader 使用缺陷的介绍,请小伙伴们移步如下链接观赏精彩的内容:
想要进一步系统地学习 Swift 开发的小伙伴们,可以来我的《Swift 语言开发精讲》专栏逛一逛哦:
在下篇博文中,我们将继续 SwiftUI 视图尺寸适配之旅,介绍更多有趣的方法,不见不散!
总结
在本篇博文中,我们介绍了为何要让 SwiftUI 容器与子视图的尺寸“唇齿相依”,并讨论了一种“最古老”的解决之道。
感谢观赏,我们下篇见!😎