【发布时间】:2020-11-14 18:20:13
【问题描述】:
在我的项目中,我必须使用带有嵌套视图的自定义对齐指南。这很复杂,但我会尝试通过以下方式简化我的案例:
有 3 种可能的视图:一个圆形、一个 upContainer 和一个 downContainer。这两个容器只是其他圆圈/容器的数组。 downContainer 仅将第一个数组与当前行对齐(例如,其他圆圈的位置),而不管第二个数组内部是什么; upContainer 则相反。这是一张图片来可视化它们:
我希望能够使用这三个元素动态构建复杂的视图。因此,我创建了一个具有嵌套关联类型的数据模型(这是一个简单的枚举):
enum Data: Hashable {
case circle
case upContainer([Data], [Data])
case downContainer([Data], [Data])
}
为了手动对齐三个元素,我以这种方式创建了自定义 SwiftUI 对齐方式:
extension VerticalAlignment {
struct MyAlignment: AlignmentID {
static func defaultValue(in context: ViewDimensions) -> CGFloat {
context[VerticalAlignment.center]
}
}
static let myAlignment = VerticalAlignment(MyAlignment.self)
}
我希望特定容器中的圆圈具有相同的颜色,因此我创建了一个扩展程序以快速生成随机颜色:
extension Color {
static var random: Color {
return Color(
red: .random(in: 0...1),
green: .random(in: 0...1),
blue: .random(in: 0...1)
)
}
}
现在我可以创建对应于三个元素的视图:
struct CirclesView: View {
var circles: [Data]
var color: Color = Color.random
var body: some View {
HStack(alignment: .myAlignment) {
ForEach(circles, id: \.self) { value in
switch value {
case .circle:
Circle()
.frame(width: 20, height: 20)
.foregroundColor(color)
case .upContainer(let firstData, let secondData):
UpContainerView(container: (firstData, secondData))
case .downContainer(let firstData, let secondData):
DownContainerView(container: (firstData, secondData))
}
}
}
}
}
struct UpContainerView: View {
var container: ([Data], [Data])
var color = Color.random
var body: some View {
VStack(alignment: .leading) {
CirclesView(circles: container.0, color: color)
CirclesView(circles: container.1, color: color)
.alignmentGuide(.myAlignment) { $0[VerticalAlignment.center] }
}
}
}
struct DownContainerView: View {
var container: ([Data], [Data])
var color = Color.random
var body: some View {
VStack(alignment: .leading) {
CirclesView(circles: container.0, color: color)
.alignmentGuide(.myAlignment) { $0[VerticalAlignment.center] }
CirclesView(circles: container.1, color: color)
}
}
}
我应该可以通过写作来做到这一点:
struct ContentView : View {
var myData: [Data] = [.circle, .circle, .downContainer(
[.circle, .circle, .circle, .downContainer(
[.circle, .circle, .circle, .circle], [.circle]
)], [.circle, .circle, .upContainer(
[.circle, .circle], [.circle, .circle, .circle, .circle]
)]
)]
var body: some View {
CirclesView(circles: myData)
}
}
然而结果是这样的:
如您所见,第一个容器(棕色容器)并未呈现为 downContainer,实际上它与前两个绿色圆圈居中对齐。这是因为即使我们只为两个数组中的一个设置显式对齐指南,SwiftUI 也会考虑另一个数组的子视图的对齐指南(即使我们想忽略它们)并且存在冲突。
struct DownContainerView: View {
var container: ([Data], [Data])
var color = Color.random
var body: some View {
VStack(alignment: .leading) {
CirclesView(circles: container.0, color: color)
.alignmentGuide(.myAlignment) { $0[VerticalAlignment.center] }
//We set the explicit alignment guide only for the first array, I am looking for a way to ignore completely the alignment guides of the second one and to calculate the alignment based only on the first one.
CirclesView(circles: container.1, color: color)
}
}
}
为了解决冲突,我们应该简单地忽略另一个数组的对齐指南。 实际的问题变成了“我们如何忽略那些由 SwiftUI 隐式自动计算的对齐指南?”
注意:我现在可以使用 PreferenceKeys 来解决这个问题,但我认为这个问题应该只使用对齐指南来解决,因为它只是一个对齐问题。我还认为一个解决方案可能是为每个子容器动态创建一个新的自定义对齐指南,但我不知道如何去做(如果在 Swift 中可能的话)。
【问题讨论】:
-
那篇文章我已经看过了,但是作者不需要处理嵌套视图,因此我的问题不会出现。至于你所说的对齐指南没有被激活,它们实际上是: CircleViews 是父视图,它定义了一个 HStack,对齐类型为“myAlignment”。然后 upContainer 和 downContainer 有两个子视图(我称之为数组),其中只有一个声明了显式指南。感谢您的关注,如果您需要任何进一步的说明,请随时询问。我知道这个问题很复杂,但我真的被困住了。
-
在 我想渲染这张图片 - 根据你的逻辑,它不能以常规 .circle 开头,因为顶部有两个蓝色,所以它应该以 .upContainer 开头.我错了吗?我仍然很难理解你的目标和逻辑。
-
再次感谢您的关注,非常感谢。我的目标是了解自定义对齐指南如何在嵌套视图中相互交互。在我要表示的图像中,最初的两个蓝色圆圈不属于容器,实际上它们仅位于一排圆圈上。相反,红色、黄色和粉红色的显示在两行上,这意味着它们在一个容器中。红色的必须是 downContainer,因为它只将顶部的圆圈与蓝色的圆圈对齐(黄色的圆圈也是如此),而粉色是“向上”的,因为它将底部的圆圈与前面的圆圈(红色)对齐。
-
好吧,正如您所描述的,我补充说您希望对齐参考线按照添加元素的顺序相互匹配,但事实并非如此。容器提供参考对齐指南。在您的情况下,它是带有 .center 的 HStack,所有内部都通过其引入的(隐式或显式)对齐指南对齐。所以,你的顶部 HStack.center 引入了一条线,所以前 2 个蓝色垂直居中(总是!!),然后你有 VStack 的 VStack,它最终也将居中,实际观察到的(至我明白了)。
-
没错!问题是“我不希望它被观察到,我该怎么做?”。我想忽略我没有明确设置的对齐指南。对于一层嵌套视图,它只是起作用,因为显式指南似乎比隐式指南具有更高的优先级(只需查看蓝色和灰色容器,它们按照我的意愿对齐),但如果子视图中有显式指南,它也很重要,我想忽略它!我希望我终于设法解释了自己,如果我不清楚,对不起。