水平堆叠内的中心项目

Center Item Inside Horizontal Stack

如果水平堆栈中有 3 个项目,我想我可以这样做:

HStack{

      Text("test")

      Spacer()

      item2()

      Spacer()

      Text("test")
}

使 item2() 在两个文本视图之间居中。然而,这个问题是 item2() 不一定总是居中,因为,假设 Text("test") 更改为 Text("a") 或其他东西。这会导致问题,第二项并不总是位于屏幕中央。

如何使 item2() 始终居中?

谢谢

您可能需要添加一些自定义的对齐组件。

extension HorizontalAlignment {
    private enum MyHAlignment: AlignmentID {
        static func defaultValue(in d: ViewDimensions) -> CGFloat {
            return d[HorizontalAlignment.center]
        }
    }
    
    static let myhAlignment = HorizontalAlignment(MyHAlignment.self)
}


HStack {
    Spacer()
    Text("jjjjjjjjjj")
    Spacer()
    
    Image("image").alignmentGuide(.myhAlignment) { (ViewDimensions) -> CGFloat in
        return ViewDimensions[HorizontalAlignment.center]
    }
    
    Spacer()     
    Text("test")       
}
.frame(alignment: Alignment(horizontal: .myhAlignment, vertical: .center))



我会建议以下起点(最简单的情况...阅读下面的原因)

正如所见,它确实提供了居中的 w/o 帧偏移和正确对齐的边元素,但是......有 缺点 - 它可以在这种最简单的变体中工作 如果事先知道那些 三个文本元素在用户 运行 时间内不应重叠。如果是这种情况(真的有这种情况),那么这种方法就可以了。但是,如果 left/right 文本可能会在 运行 时间内增长,那么将需要更多的计算来将它们的宽度限制为 .frame(maxWidth:),具体取决于居中元素的宽度...该变体更复杂, 但这是可行的。

var body: some View {
        ZStack {
            HStack {
                Text("Longer side")
                Spacer()
                Text("One")
            }
            item2()
        }
    }

private func item2() -> some View {
    Text("CENTER")
        .background(Color.yellow)
        .border(Color.red)
}

更新: 这是一种可能的方法,可以限制其中一侧不与居中的一侧重叠(包含异步更新,因此应在 实时预览 或模拟器)

所以...如果左侧文本是动态的并且需要剪切尾随符号,那么它可以这样...

它会自动适应设备方向的变化

struct TestHorizontalPinCenter: View {

    @State var centerFrame: CGRect = .zero
    
    private let kSpacing: CGFloat = 4.0
    var body: some View {
            ZStack {
                HStack {
                    Text("Longer side very long text to fit")
                        .lineLimit(1)
                        .frame(maxWidth: (centerFrame == .zero ? .infinity : centerFrame.minX - kSpacing), alignment: .leading)

                    Spacer()
                    
                    Text("One")
                }
                item2()
                    .background(rectReader($centerFrame))
            }
        }
    
    private func item2() -> some View {
        Text("CENTER")
            .background(Color.yellow)
            .border(Color.red)
    }

    func rectReader(_ binding: Binding<CGRect>) -> some View {
        return GeometryReader { (geometry) -> AnyView in
            let rect = geometry.frame(in: .global)
            DispatchQueue.main.async {
                binding.wrappedValue = rect
            }
            return AnyView(Rectangle().fill(Color.clear))
        }
    }
}

而如果需要左侧包裹,则.lineLimit(nil)需要额外的布局,解决方案增长,但思路是一样的。希望这会对某人有所帮助。

backup

要使视图居中,您可以使用 ZStack:

ZStack {
    item2()

    HStack {
        Text("test")
        Spacer()
        Text("test")
    }
}

我遇到了同样的问题,@Asperi 的解决方案有效,但我遇到了多行文本问题,如果我在列表中使用它,还会出现一些性能问题。

以下解决方案解决了所有问题。

HStack(alignment: .center) {
  Text("test")
      .frame(maxWidth: .infinity)
  item2()
  Text("test")            
      .frame(maxWidth: .infinity)
}