您可以使用
overlay(alignment:content:)
修饰符(以前的
overlay(_:alignment:)
)与自定义
对齐指南相结合来实现此操作。
基本思路是将您的
参考视图的
底部与弹出视图的
顶部对齐。
令人烦恼的是,覆盖修饰符只允许您指定一个对齐指南(用于这两个视图)。因此,如果您写
stack1.overlay(alignment: .bottom) { stack2 }
,它将使您的参考视图底部与覆盖视图底部对齐。快速解决这个问题的方法是覆盖覆盖层的底部对齐指南,并返回其顶部。
referenceView
.overlay(alignment: .bottom) {
popoverContent
.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)