SwiftUI - 将叠加视图相对于其锚点定位

3

我有一个包含两个视图的ZStack:

  • referenceContent - 包含一些内容和一个分隔线。这是跨越整个屏幕的主要内容。

  • popoverContent - 是一个条件弹出窗口,只占据屏幕的一小部分。

var body: some View {
  ZStack {
    referenceContent

    if popoverCondition {
      popoverContent
    }
  }
}

我希望弹出内容的顶部与参考内容的底部对齐。

有人知道如何实现吗?或者是否有比我现在做的更好的查看弹出窗口的方法?谢谢!


这个问题已经提供了足够的信息,答案也已经反映出来了。投票重新开放。 - par
1个回答

9
您可以使用overlay(alignment:content:)修饰符(以前的overlay(_:alignment:))与自定义对齐指南相结合来实现此操作。
基本思路是将您的参考视图底部与弹出视图的顶部对齐。
令人烦恼的是,覆盖修饰符只允许您指定一个对齐指南(用于这两个视图)。因此,如果您写stack1.overlay(alignment: .bottom) { stack2 },它将使您的参考视图底部与覆盖视图底部对齐。快速解决这个问题的方法是覆盖覆盖层的底部对齐指南,并返回其顶部。
referenceView
  .overlay(alignment: .bottom) {
    popoverContent
      // overwrites bottom alignment of the popover with its top alignment guide.
      .alignmentGuide(.bottom) {$0[.top]}
  }

Overlay与ZStack

你可能会问:"为什么不使用ZStack而选择overlay?"。这两者的区别在于,当布局您的整个视图(参考+弹出窗)时,ZStack将考虑弹出窗的大小。这正好与弹出窗应该做的相反。对于弹出窗,布局系统只应该考虑您的参考视图的大小,并在其之上绘制弹出窗(而不影响参考视图的布局)。这正是overlay(...)修饰器所做的。

旧版API(iOS 15及macOS 12之前)

在旧版SwiftUI中,overlay修饰器的参数顺序是相反的。因此,这些旧系统的代码示例如下:

referenceView
  .overlay(
    popoverContent.alignmentGuide(.bottom) {$0[.top]},
    alignment: .bottom
  )

自定义对齐参考线

当你不想覆盖现有的对齐参考线(例如,因为你需要在其他地方使用它)时,你也可以使用自定义的对齐参考线。下面是一个更通用的示例,使用了名为Alignment.TwoSided的自定义对齐参考线。

extension View {
    @available(iOS 15.0, *)
    func overlay<Target: View>(align originAlignment: Alignment, to targetAlignment: Alignment, of target: Target) -> some View {
        let hGuide = HorizontalAlignment(Alignment.TwoSided.self)
        let vGuide = VerticalAlignment(Alignment.TwoSided.self)
        return alignmentGuide(hGuide) {$0[originAlignment.horizontal]}
            .alignmentGuide(vGuide) {$0[originAlignment.vertical]}
            .overlay(alignment: Alignment(horizontal: hGuide, vertical: vGuide)) {
                target
                    .alignmentGuide(hGuide) {$0[targetAlignment.horizontal]}
                    .alignmentGuide(vGuide) {$0[targetAlignment.vertical]}
            }
    }
}

extension Alignment {
    enum TwoSided: AlignmentID {
        static func defaultValue(in context: ViewDimensions) -> CGFloat { 0 }
    }
}

您可以像这样使用它:
referenceView
  .overlay(align: .bottom, to: .top, of: popoverContent)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接