简体   繁体   English

SwiftUI:无法将图像对齐到 ZStack 的顶部

[英]SwiftUI: Can't align image to the top of ZStack

I can't figure out how to align Image view on top of ZStack , by default views in SwiftUI are placed at the center of their parent, and we then use stacks to align them, I have the following piece of code:我不知道如何在ZStack顶部对齐Image视图,默认情况下 SwiftUI 中的视图放置在其父级的中心,然后我们使用堆栈来对齐它们,我有以下代码:

struct ContentView: View {
    var body: some View {
        ZStack {
           Image("bgImage")
           Text("Hello, World!")
        }
    .frame(maxWidth: .infinity, maxHeight: .infinity)
        .background(Color.red) //this is for debugging purposes, to show the area of the ZStack
    }
}

How can I position the image to the top ?如何将图像定位到顶部?

To tell the ZStack to align things a particular way within it, configure it with the alignment parameter:要告诉 ZStack 在其中以特定方式对齐事物,请使用alignment参数对其进行配置:

ZStack(alignment: .top) {
    Color.clear
    Image(...)
    Text("Hello, World!")
}

(Color.clear expands to fill all available space, so this forces your ZStack to be as large as the enclosing view without needing to add a .frame() .) (Color.clear 扩展以填充所有可用空间,因此这会强制您的 ZStack 与封闭视图一样大,而无需添加.frame() 。)

That will align everything at the top of course, which might not be what you want.当然,这会将所有内容对齐在顶部,这可能不是您想要的。 You could fix that by making a nesting your ZStacks to align as you want them to:你可以通过嵌套你的 ZStacks 来解决这个问题:

ZStack{
    ZStack(alignment: .top) {
        Color.clear
        Image(...)        // This will be at the top
    }
    Text("Hello, World!") // This will be centered
}

That said, I'd probably use a .background for this example.也就是说,我可能会在此示例中使用.background

ZStack {
    Color.clear
    Text("Hello, World!")
}
.background(Image(...), alignment: .top)

And if you only have one view, you can get rid of the ZStack and use a frame instead:如果你只有一个视图,你可以摆脱 ZStack 并使用一个框架来代替:

Text("Hello, World!")
    .frame(maxHeight: .infinity)
    .background(Image(uiImage:#imageLiteral(resourceName: "image.jpg")), 
                alignment: .top)

Keep in mind that in this case the image will draw outside its frame.请记住,在这种情况下,图像将绘制在其框架之外。 In many cases that's fine (and it's completely legal), but it can matter sometimes (for example, if you put this inside a stack).在许多情况下这很好(而且它完全合法),但有时它可能很重要(例如,如果你把它放在一个堆栈中)。 You can add .border(Color.green) to the end to see how that works.您可以在末尾添加.border(Color.green)以查看其工作原理。


This example really gets to the heart of SwiftUI layout, so it's worth understanding what's going on.这个例子真正触及了 SwiftUI 布局的核心,所以值得了解发生了什么。 This isn't a workaround or a trick, so you should get to the place where this feels very normal.这不是解决方法或技巧,因此您应该到达感觉非常正常的地方。

The top-level content view (the one that contains the ZStack) offers its entire space to the ZStack.顶级内容视图(包含 ZStack 的视图)将其整个空间提供给 ZStack。 A ZStack is always exactly the size that contains its contents, so first the ZStack needs to layout its children. ZStack 总是与包含其内容的大小完全相同,因此首先 ZStack 需要对其子项进行布局。 It lays them out according to its alignment, and then sizes itself exactly to fit around them.它根据对齐方式对它们进行布局,然后精确调整自身大小以适应它们。 So with top-alignment (but without Color.clear), the Image is at the top of the ZStack.因此,使用顶部对齐(但没有 Color.clear),图像位于 ZStack 的顶部。 The ZStack is just exactly the same size as the Image. ZStack 与 Image 的大小完全相同。

The top-level content view then places the ZStack in its center.然后,顶级内容视图将 ZStack 置于其中心。

The way the ZStack lays out its children is similar to how the content view did. ZStack 布置其子项的方式类似于内容视图的方式。 It offers all the space it was offered, and then the child-views decide their sizes.它提供了它所提供的所有空间,然后子视图决定它们的大小。 Views always decide their own sizes.视图总是决定自己的大小。 The Image and Text are fixed-sized views, so they are just the size of their contents. Image 和 Text 是固定大小的视图,因此它们只是其内容的大小。 But Color is a flexible-sized view.但是 Color 是一个灵活大小的视图。 It accepts the entire space that the ZStack offered (which is the same space that the top-level content view offered) and returns that as its size.它接受 ZStack 提供的整个空间(与顶级内容视图提供的空间相同)并将其作为其大小返回。 Since a ZStack must exactly contain its children, the ZStack is now the size of the top-level content view, and things behave as you expect, aligning at the top of the screen.由于 ZStack 必须准确地包含其子项,因此 ZStack 现在是顶级内容视图的大小,并且事情的行为与您预期的一样,在屏幕顶部对齐。

Let's compare to using .frame() as you originally did:让我们与最初使用的.frame()进行比较:

ZStack {
   Image("bgImage")
   Text("Hello, World!")
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color.red) //this is for debugging purposes, to show the area of the ZStack

First, I want to focus on your comment, because it's not correct.首先,我想关注您的评论,因为它不正确。 This is not the area of the ZStack.这不是 ZStack 的区域。 The ZStack is exactly the size of its contents (the Image and the Text). ZStack 正是其内容(图像和文本)的大小。 You put the background on the frame view, and the frame view is larger.您将背景放在框架视图上,框架视图更大。

A key confusion people have is that they think .frame(...) changes the size of the View it's attached to.人们的一个主要困惑是他们认为.frame(...)改变了它所附加的 View 的大小。 That's not correct at all.这根本不正确。 As before, a ZStack is always the size of its contents.和以前一样, ZStack 始终是其内容的大小。 .frame() creates a completely new view of the requested size. .frame()创建请求大小的全新视图。 It then positions the wrapped view inside itself according to the frame's alignment.然后它根据框架的对齐方式将包裹的视图定位在自身内部。 So in this example it works like this:所以在这个例子中它是这样工作的:

Top-level - Background - Frame - ZStack - { Image Text }顶级 - 背景 - 框架 - ZStack - {图像文本}

The top-level view offers all its space to the Background.顶层视图为背景提供了所有空间。 Backgrounds are the size of what they contain, so it offers all of that space to the Frame.背景是它们所包含内容的大小,因此它为框架提供了所有空间。 The Frame is flexible in both directions (due to the max fields), and so it ignores its child's size and chooses to be the size it was offered. Frame 在两个方向上都是灵活的(由于max字段),因此它忽略其子项的大小并选择它提供的大小。

The Frame then offers all that space to the ZStack.然后 Frame 将所有空间提供给 ZStack。 The ZStack lays out its children, and returns its size as exactly the size that contains them. ZStack 布置它的孩子,并返回它的大小作为包含它们的大小。

The Frame then places the ZStack according to the Frame's alignment ( .center , since that's the default).然后 Frame 根据 Frame 的对齐方式放置 ZStack( .center ,因为这是默认设置)。 If you'd set the Frame's alignment to .top , then the ZStack would have been placed at the top of the frame (but the text would be centered in the ZStack not in the Frame).如果您将 Frame 的对齐方式设置为.top ,则 ZStack 将被放置在框架的顶部(但文本将在 ZStack中而不是在 Frame 中居中)。

It then reports to the Background that it is as large as the top-level view (since its flexible).然后它向后台报告它与顶级视图一样大(因为它很灵活)。

The Background then claims that same size to the top-level content view.然后背景声明与顶级内容视图相同的大小。

And finally, the top-level content view places the Background in its center.最后,顶级内容视图将背景置于其中心。

You could always just put the things you want to be at the top in a VStack and use a Spacer.你总是可以把你想要的东西放在 VStack 的顶部并使用 Spacer。

ZStack(){
    Image(...)
    Spacer()
}

The complete code should look something like this:完整的代码应如下所示:

struct ContentView: View {
    var body: some View {
        ZStack(){
            Text("Hello, World!")
        
            VStack {
                Image(...)
                Spacer()
            } 
        }
    }
}

You could do this with HStacks as well.你也可以用 HStacks 做到这一点。 Important to notice that if the image has no limits to its size, it will always take up as much space as possible.重要的是要注意,如果图像对其大小没有限制,它将始终占用尽可能多的空间。 That would remove the purpose of the Spacer.这将消除 Spacer 的用途。 Hope this helps :-)希望这可以帮助 :-)

So one thing working against you is the infinity maxHeight modifier, assuming that you do not want some space between the image and the bottom of the view.因此,对您不利的一件事是 infinity maxHeight 修饰符,假设您不希望图像和视图底部之间有一些空间。

.edgesIgnoringSafeArea(.all)

You may just need to tell your ZStack to ignore safe area insets.您可能只需要告诉您的 ZStack 忽略安全区域插图。

struct ContactsView: View {
  var body: some View {

    ZStack {
      Image("bgImage")
      Text("Hello, World!")
    }.frame(maxWidth: .infinity, maxHeight: .infinity).background(Color.red).edgesIgnoringSafeArea(.all)

  }
}

If you need space between the bottom and the image, wrap the ZStack in a VStack and throw a Spacer in the bottom of the VStack.如果您需要底部和图像之间的空间,请将 ZStack 包裹在 VStack 中,并在 VStack 的底部放置一个 Spacer。

struct ContactsView: View {
  var body: some View {
    VStack {

      ZStack {
        Image(systemName: "bgImage")
        Text("Hello, World!")
      }.frame(maxWidth: .infinity, maxHeight: 300).background(Color.red)

      Spacer()

    }.edgesIgnoringSafeArea(.all)
  }
}
ZStack {
    Image("background")
         .resizable()
         .scaledToFill()
         .edgesIgnoringSafeArea(.all)
}

Add .edgesIgnoringSafeArea(.all)添加 .edgesIgnoringSafeArea(.all)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM