正确的答案是优雅,使用原生 SwiftUI 方法,并允许以任何方式全屏呈现任何类型的视图,任何类型的视图上方如下。 所有其他答案都使用 3rd-party 包,以不可恢复的方式利用 UIKit,或者不优雅。
首先,您需要将要隐藏的视图声明为允许全屏覆盖的视图。一般的想法是,全屏但您想要隐藏的视图应该有一个首选项键,该键将存储要在其顶部显示的视图:
/// A preference that allows you to show full screen overlays on top of any top-level full screen view.
///
/// This is particularly useful when trying to present views over a `TabView` or a `NavigationView` which block placing views over
/// their respective safe zones (like the tab bar or the navigation bar).
struct FullScreenCoverPreferenceKey: PreferenceKey {
typealias Value = [OverlayView]
static var defaultValue: [OverlayView] = []
static func reduce(value: inout [OverlayView], nextValue: () -> [OverlayView]) {
value.append(contentsOf: nextValue())
}
/// The underlying overlay view behind the preference key.
struct OverlayView: View, Identifiable {
let id = UUID()
var content: AnyView
init<Content: View>(@ViewBuilder content: () -> Content) {
self.content = AnyView(content())
}
var body: some View {
content
}
}
}
FullScreenCoverPreferenceKey.value 将包含要在全屏视图顶部显示的视图数组(例如 TabView)。
然后,使用以下方法扩展View 协议,以便将首选项键附加到视图:
extension View {
/// Declares that this view is capable of receiving full screen overlays.
///
/// - Important
/// This modifier should only be used on views that are taking up the entire screen.
func allowFullScreenOverlays() -> some View {
self
.overlayPreferenceValue(FullScreenCoverPreferenceKey.self) { overlayViewsArray in
ZStack {
ForEach(overlayViewsArray) { overlayView in
overlayView
}
}
}
}
}
将overlayPreferenceValue 的返回视图呈现为由preferenceKey 的[OverlayView] 数组构成的ZStack,允许我们将多个叠加层堆叠在一起。
接下来,创建任何类型的叠加层并将其附加到 TabView 的任何子视图:
ZStack {
Color.white.ignoresSafeArea() //background if needed
TabView {
NavigationView {
ZStack {
NavigationLink(
destination:
ZStack { Color.orange }
.transformPreference(FullScreenCoverPreferenceKey.self) {
$0.append(FullScreenCoverPreferenceKey.OverlayView {
//The overlay
ZStack {
Color.green
Text("I am full screen!")
}
})
},
label: {
Text("Press me to show full screen!")
})
}
}
.tabItem {
Text("Tab")
}
}
}
.allowFullScreenOverlays()
如果你喜欢这样,你可以把这种丑陋的.transformPreference() 调用隐藏在另一个视图修饰符后面:
extension View {
/// Adds a full screen overlay to the view.
///
/// Important: The view must have a parent with `.allowsFullScreenOverlays()` set.
func fullScreenOverlay<Content: View>(@ViewBuilder content: @escaping () -> Content) -> some View {
self
.transformPreference(FullScreenCoverPreferenceKey.self) {
$0.append(FullScreenCoverPreferenceKey.OverlayView {
content()
})
}
}
最终的视图将如下所示,Tab 被完全覆盖: