ZStack内部定位

Positioning inside ZStack

提问人:eugene_prg 提问时间:1/5/2021 最后编辑:eugene_prg 更新时间:1/6/2021 访问量:12228

问:

我希望将这个红色矩形重新定位到左下角,但由于某种原因,我的对齐方式和填充方式无法按预期工作。我做错了什么,如何将其移动到左下角?

法典:

struct ContentView: View {
    
    var body: some View {

        ZStack {
                VStack(spacing: 0) {
                    Rectangle()
                        .fill(Color.red)
                        .frame(width: 197, height: 163)
                }
                .frame(width: 284, height: 269)
                .padding(.leading, 0)
                .padding(.bottom, 0)
                .background(Color.blue)
            }
    }
}
swiftui-zstack

评论


答:

9赞 YodagamaHeshan 1/5/2021 #1

ZStack有一个参数,它是 两个轴上的对齐方式。alignment: Alignment

  struct ContentView: View {
        
        var body: some View {
            
            ZStack(alignment:.bottomLeading) {
                Rectangle()
                    .fill(Color.blue)
                    .frame(width: 284, height: 269)
                Rectangle()
                    .fill(Color.red)
                    .frame(width: 197, height: 163)
            }
            
            
        }
    }
20赞 Rob Napier 1/6/2021 #2

Yodagama 的答案可能是你想要的,但也值得探索你的困惑,因为 SwiftUI 布局并不像你想象的那样工作。调用 like 不会“设置项目的大小”,也不会“设置项目的颜色”。SwiftUI 中的几乎所有内容都会创建一个全新的 View,该视图负责放置其子项。在许多情况下,这些感觉就像是一回事,但它们非常不同。.frame(...).fill(...)

我将分解代码中发生的事情,从内部开始并逐步解决。

                Rectangle()

这将创建一个 Rectangle,用于填充它给定的任何空间。

                Rectangle()
                    .fill(Color.blue)

这会将 Rectangle 嵌入到新视图中,该视图将环境中的“填充”颜色设置为蓝色。

                Rectangle()
                    .fill(Color.blue)
                    .frame(width: 197, height: 163)

这会将其嵌入到具有固定大小 (197x163) 的新视图中。嵌入的视图居中,但由于 Rectangle 占用了所有可用空间,因此它填充了 Frame。

            VStack(spacing: 0) {
                Rectangle()
                    .fill(Color.red)
                    .frame(width: 197, height: 163)
            }

这会将该 Frame 嵌入到一个 VStack 中,该 VStack 在其元素之间没有间距(但这并不重要,因为只有一个)。VStack 的大小正好是其子级 (197x163) 的大小。

            VStack(spacing: 0) {
                Rectangle()
                    .fill(Color.red)
                    .frame(width: 197, height: 163)
            }
            .frame(width: 284, height: 269)

这会将 197x163 VStack 居中到固定为 284x269 的新视图中。它不会将 VStack 的大小设置为 284x269。VStacks 的大小总是与它们的子级完全相同。这是您出错的具体位置(在完成其余部分后,我将解释如何解决它)。

            VStack(spacing: 0) {
                Rectangle()
                    .fill(Color.red)
                    .frame(width: 197, height: 163)
            }
            .frame(width: 284, height: 269)
            .padding(.leading, 0)
            .padding(.bottom, 0)

这将创建一个嵌入 284x269 帧的新视图,左侧有 0 个额外的填充(因此这个新视图的大小与当前帧完全相同,并且不执行任何操作)。然后,它将其嵌入到一个新的视图中,该视图在底部添加了 0 个额外的填充。同样,这根本没有做任何事情。

           VStack(spacing: 0) {
                Rectangle()
                    .fill(Color.red)
                    .frame(width: 197, height: 163)
            }
            .frame(width: 284, height: 269)
            .padding(.leading, 0)
            .padding(.bottom, 0)
            .background(Color.blue)

这会将所有这些内容嵌入到一个新视图中,该视图绘制的蓝色背景与嵌入视图 (284x269) 的大小完全相同。

    ZStack {
            VStack(spacing: 0) {
                Rectangle()
                    .fill(Color.red)
                    .frame(width: 197, height: 163)
            }
            .frame(width: 284, height: 269)
            .padding(.leading, 0)
            .padding(.bottom, 0)
            .background(Color.blue)
        }
}

最后,这将所有这些集中在ZStack中。ZStack 的大小正好是包装其子项所需的大小,因此是 284x269。这根本没有任何作用。


那么你如何解决这个问题。你出错的地方是 VStack 在其周围框架中的位置。它居中,但你希望它对齐。这里不需要 ZStack,甚至不需要 VStack。你这样做是这样的:

    Rectangle()
        .fill(Color.red)
        .frame(width: 197, height: 163)
        .frame(width: 284, height: 269, alignment: .bottomLeading) // <===
        .background(Color.blue)

请注意如何连续创建两个框架视图。第一个边界 Rectangle。第二个将其嵌入并对齐到一个更大的框架中。这是一个很好的例子,说明SwiftUI与开发者想象的如此不同。事实上,这可以进一步简化,因为 Color 会填充其提供的所有空间,就像 Rectangle 一样:

    Color.red
        .frame(width: 197, height: 163)
        .frame(width: 284, height: 269, alignment: .bottomLeading)
        .background(Color.blue)

但我经常发现,如果可能的话,使用垫片可以做得更好,更灵活。根据我的经验,当您没有固定的框架尺寸时,垫片效果更好。(我并不是说这一定是最佳实践;这只是我的经验。SwiftUI 是相当新的,我们仍在弄清楚什么是最佳实践。

    VStack {
        Spacer()    // Fill all space above
        HStack {
            Rectangle()
                .fill(Color.red)
                .frame(width: 197, height: 163)
            Spacer()    // Fill all space to the right
        }
    }
    .frame(width: 284, height: 269)
    .background(Color.blue)

评论

2赞 eugene_prg 1/6/2021
Rob,感谢您花时间和精力向我解释这一点,我花了 1 步才能理解 swiftui 的工作原理
1赞 Serg 5/1/2022
多谢!惊人的答案。我从没想过 .frame 修饰符可以这样使用。
1赞 jboi 8/24/2022
很酷的答案。虽然我同意这更直观,但在国际环境中特别有用。 可能在右侧,并由 SwiftUI 自动处理。Spaceralignment.leading