45

如何将View( Image)水平居中HStack?我想要一个按钮左对齐并且图像水平居中视图。

目前我有这个结构:

VStack {
        HStack {
            Button(action: {
                print("Tapped")
            }, label: {
                Image("left-arrow")
                    .resizable()
                    .frame(width: 30, height: 30, alignment: .leading)
            }).padding(.leading, 20)
            
            Spacer()
            
            Image("twitter-logo")
                .resizable()
                .frame(width: 30, height: 30, alignment: .center)
            
        }
        Spacer()
    }

这给了我这个:

在此处输入图像描述

但我想实现这一点:

在此处输入图像描述

4

6 回答 6

48

您可以在 a 中嵌入两个HStack'sZStack并相应地放置垫片以用于水平间距。将所有内容嵌入到 a 中VStackSpacer()以将所有内容推到顶部。

struct ContentView : View {
    var buttonSize: Length = 30
    var body: some View {
        VStack {
            ZStack {
                HStack {
                    Button(action: {
                        
                    }, label: {
                        Image(systemName: "star")
                            .resizable()
                            .frame(width: CGFloat(30), height: CGFloat(30), alignment: .leading)
                    }).padding(.leading, CGFloat(20))
                    
                    Spacer()
                }
                
                HStack {
                    Image(systemName: "star")
                        .resizable()
                        .frame(width: CGFloat(30), height: CGFloat(30), alignment: .center)
                }
            }
            Spacer()
        }
    }
}

注意:在第二个HStack中,图像应该自动居中对齐,但如果不是,您可以Spacer()在图像前后放置一个。

编辑:添加VStackandSpacer()将所有内容移到顶部,就像 OP 想要的那样。

编辑 2:删除了图像上的填充,因为它导致图像从中心略微偏移。由于它是独立的HStack并且居中对齐,因此不需要填充。

编辑 3:感谢评论中的@Chris Prince,我决定制作一个简单的 NavigationBar 式自定义视图,您可以提供左、中和右参数来创建 OP 所需的效果(每组视图对齐彼此独立):

struct CustomNavBar<Left, Center, Right>: View where Left: View, Center: View, Right: View {
    let left: () -> Left
    let center: () -> Center
    let right: () -> Right
    init(@ViewBuilder left: @escaping () -> Left, @ViewBuilder center: @escaping () -> Center, @ViewBuilder right: @escaping () -> Right) {
        self.left = left
        self.center = center
        self.right = right
    }
    var body: some View {
        ZStack {
            HStack {
                left()
                Spacer()
            }
            center()
            HStack {
                Spacer()
                right()
            }
        }
    }
}

用法

struct ContentView: View {
    let buttonSize: CGFloat = 30
    var body: some View {
        VStack {
            CustomNavBar(left: {
                Button(action: {
                    print("Tapped")
                }, label: {
                    Image(systemName: "star")
                        .resizable()
                        .frame(width: self.buttonSize, height: self.buttonSize, alignment: .leading)
                }).padding()
            }, center: {
                Image(systemName: "star")
                    .resizable()
                    .frame(width: 30, height: 30, alignment: .center)
            }, right: {
                HStack {
                    Text("Long text here")
                    Image(systemName: "star")
                        .resizable()
                        .frame(width: 30, height: 30, alignment: .center)
                        .padding(.trailing)
                }.foregroundColor(.red)
            })
            Spacer()
            Text("Normal Content")
            Spacer()
        }
    }
}

模拟器上显示的示例代码

于 2019-06-05T15:31:13.507 回答
25

将按钮大小保存到属性并为图像添加负填充是什么意思?并注意图像后的附加垫片。

struct ContentView: View {

    var buttonSize: Length = 30

    var body: some View {
        VStack {
            HStack {
                Button(action: {
                    print("Tapped")
                }, label: {
                    Image(systemName: "star")
                        .resizable()
                        .frame(width: buttonSize, height: buttonSize, alignment: .leading)
                })
                Spacer()
                Image(systemName: "star")
                    .resizable()
                    .frame(width: 30, height: 30, alignment: .center)
                    .padding(.leading, -buttonSize)
                Spacer()
            }
            Spacer()
        }
    }
}

结果:

在此处输入图像描述

于 2019-06-05T15:25:35.030 回答
9

对我来说最简单的方法:

ZStack(){
    HStack{
        Image("star").resizable().foregroundColor(.white).frame(width: 50, height: 50)
        Spacer()
    }
        Image("star").resizable().font(.title).foregroundColor(.white).frame(width: 50, height: 50)
}
于 2020-06-19T06:06:09.417 回答
6

您使用位置属性将视图居中尝试此代码

Group{ // container View
   Image("twitter-logo")
            .resizable()
            .frame(width: 30, height: 30, alignment: .center)
}.position(x: UIScreen.main.bounds.width/2)
于 2019-12-04T06:14:25.467 回答
0

让我提出一个不同的解决方案:

https://gist.github.com/buscarini/122516641cd0ee275dd367786ff2a736

它可以这样使用:

HStack {
    Color.red
        .frame(width: 0, height: 50)
        .layoutPriority(1)
        
    GlobalHCenteringView {
        Text("Hello, world!")
            .lineLimit(1)
            .background(Color.green)
    }
    .background(Color.yellow)
    
    Color.red
        .frame(width: 180, height: 50)
        .layoutPriority(1)
    }
}

如果合适,这将使子视图在屏幕中居中,如果不合适,则保持原样。它目前正在使用 UIScreen,因此它仅适用于 iOS,但您可以轻松地将屏幕或父宽度传递给视图的构造函数,从 GeometryReader 或其他任何东西中获取。

于 2022-02-21T07:56:05.387 回答
0

我有一个替代解决方案。我使用隐藏的图像作为占位符。

HStack {
    Image("left-arrow").padding()
    Spacer()
    Image("twitter-logo")
    Spacer()
    // placeholder to keep layout symmetric
    Image("left-arrow").padding().hidden()
}

当然,您可以根据需要将图像替换为按钮或其他视图。

于 2022-02-04T15:21:23.907 回答