Append/prepend HStack 中的一个项目,同时保留其原始中心

Append/prepend an item in an HStack while retaining its original center

我目前有一个居中对齐的简单 VStack:

VStack {
  Image(systemName: "arrow.down")
    .foregroundColor(.red)
  Text("AAA")
}

(注意红色箭头如何与标签的中心完美对齐。)

现在我想向文本行添加另一个项目(例如使用 HStack),但“AAA”仍位于容器的中心:

VStack {
  Image(systemName: "arrow.down")
    .foregroundColor(.green)

  HStack {
    Image(systemName: "star")
    Text("AAA")
  }
}

问题是它不再居中(箭头未对齐的事实证明了这一点):


尝试 1:对齐参考线

我试图通过创建自定义对齐指南来解决此问题:

extension HorizontalAlignment {
  struct CustomAlignment: AlignmentID {
    static func defaultValue(in context: ViewDimensions) -> CGFloat {
      return context[HorizontalAlignment.center]
    }
  }

  static let custom = HorizontalAlignment(CustomAlignment.self)
}
VStack(alignment: .custom) {
  Image(systemName: "arrow.down")
    .foregroundColor(.blue)
    .alignmentGuide(.custom, computeValue: { d in d[HorizontalAlignment.center] })

  HStack {
    Image(systemName: "star")
    Text("AAA")
      .alignmentGuide(.custom, computeValue: { d in d[HorizontalAlignment.center] })
  }
}

尽管箭头现在已正确对齐,但它移动了箭头而不是将箭头保持在原处并移动了 HStack:

(注意在这张图片中,虽然蓝色箭头与“AAA”对齐,但它不再位于中心(即与其他箭头对齐)。)


尝试 #2:对齐参考线 + 偏移

我可以通过硬编码偏移来临时解决这个问题:

VStack(alignment: .custom) {
    Image(systemName: "arrow.down")
      .foregroundColor(.yellow)
      .alignmentGuide(.custom, computeValue: { d in d[HorizontalAlignment.center] })

    HStack {
      Image(systemName: "star")
      Text("AAA")
        .alignmentGuide(.custom, computeValue: { d in d[HorizontalAlignment.center] })
    }
  }.offset(x: -15)

现在看起来是正确的:

问题是我需要硬编码这个 -15 值,使用偏移量并不理想。


尝试 #3:隐藏视图

另一种解决方法是在末尾添加一个相同大小的隐藏视图。

VStack {
  Image(systemName: "arrow.down")
    .foregroundColor(.purple)

  HStack {
    Image(systemName: "star")
    Text("AAA")
    Image(systemName: "star")
      .opacity(0)
  }
}

我宁愿避免使用隐藏视图的解决方案。它还使 HStack 比实际需要的更大。


有没有其他方法可以使“AAA”与屏幕中心对齐,但在其左侧添加星号?

我想避免对任何值进行硬编码,因为标签或图像可能有一天会发生变化。

struct ContentView: View {
    
    @State private var offset: CGFloat = .zero
    
    var body: some View {
        VStack {
            Image(systemName: "arrow.down")
                .foregroundColor(.green)
            
            HStack(spacing: 0) {
                Image(systemName: "star")
                    .overlay(
                        GeometryReader { geo in
                            Color.clear.onAppear {
                                offset = geo.size.width
                            }
                        }
                    )

                Text("AAA")
            }
            .offset(x: -offset/2)
        }
    }
}

解决此问题的一种方法(也许是最简单的方法)是使用您的自定义对齐指南,并使向下箭头图像占据所有可用的水平方向 space。

您可以通过添加 .frame(maxWidth: .infinity).

来做到这一点
VStack(alignment: .custom) {
  Image(systemName: "arrow.down")
    .foregroundColor(.gray)
    .alignmentGuide(.custom, computeValue: { d in d[HorizontalAlignment.center] })
    .frame(maxWidth: .infinity) // Add frame here

  HStack {
    Image(systemName: "star")
    Text("AAA")
      .alignmentGuide(.custom, computeValue: { d in d[HorizontalAlignment.center] })
}

这给出了以下输出:

如果您需要使向下箭头图像可调整大小,请确保在使用 maxWidth 设置框架之前根据图像大小设置框架。

Image(systemName: "arrow.down")
  .resizable()
  .frame(width: 50, height: 50)
  .foregroundColor(.gray)
  .alignmentGuide(.custom, computeValue: { d in d[HorizontalAlignment.center] })
  .frame(maxWidth: .infinity)