You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SwiftUI中如何判断视图是在Popover还是Sheet中呈现

解决方案

horizontalSizeClass判断失效的核心原因是:Popover弹出后,系统会自动为Popover内的视图层级设置独立的compact宽度尺寸类,和触发弹出的父视图层级的尺寸类不一致,在被弹出视图内部读取该值,自然无法准确匹配系统选择的展示样式。
不要在被弹出的视图内部猜测当前展示形式,最稳定的实现是在弹出入口处,基于父视图层级的尺寸类(系统本身就是依据这个值决定用Popover还是Sheet的),明确给被弹出视图传递展示类型标记。


具体实现步骤

  • 自定义环境值,用于全局传递展示类型标记,避免多层级视图层层传参的麻烦:
private enum PresentedAsSheetEnvironmentKey: EnvironmentKey {
    static let defaultValue = false
}

extension EnvironmentValues {
    var isPresentedAsSheet: Bool {
        get { self[PresentedAsSheetEnvironmentKey.self] }
        set { self[PresentedAsSheetEnvironmentKey.self] = newValue }
    }
}
  • 修改SettingsView逻辑,直接从环境中读取展示标记判断是否显示Done按钮,不再依赖视图内部的尺寸类值:
struct SettingsView: View {
    fileprivate let internalSettings = SettingsViewInternal()
    @Environment(\.dismiss) var dismiss
    @Environment(\.isPresentedAsSheet) var isPresentedAsSheet
    
    var body: some View {
        NavigationStack {
            internalSettings
                .navigationTitle("Settings")
                .toolbar {
                    ToolbarItem(placement: .navigationBarLeading) {
                        Button("Reset") {
                            internalSettings.reset()
                        }
                    }
                    if isPresentedAsSheet {
                        ToolbarItem(placement: .confirmationAction) {
                            Button("Done") {
                                dismiss()
                            }
                        }
                    }
                }
        }
    }
}
  • 在触发弹出的父视图中,读取父视图层级自身的horizontalSizeClass,弹出时给被弹出视图注入对应环境值:
struct AnchorView: View {
    // 这里读取的是父视图层级的尺寸类,和系统选择弹出样式的判断依据完全一致
    @Environment(\.horizontalSizeClass) private var parentSizeClass
    @State private var settingsPresented = false
    
    var body: some View {
        Button {
            settingsPresented = true
        } label: {
            Image("gears")
        }
        .popover(isPresented: $settingsPresented) {
            SettingsView()
                // 父级尺寸类为compact时系统会自动将Popover降级为Sheet展示
                .environment(\.isPresentedAsSheet, parentSizeClass == .compact)
        }
    }
}

注意事项

  • 该方案兼容所有iOS版本,不受系统Popover内部实现调整影响:iOS 15及以下系统的.popover在compact宽度下会自动降级为Sheet样式,iOS 16+的.presentation自适应弹出逻辑也同样基于父层级尺寸类判断,判断逻辑和系统行为完全匹配。
  • 不要尝试在被弹出视图内部通过遍历视图层级、读取UIViewController的modalPresentationStyle来判断,不同系统版本的Popover内部视图层级结构有差异,会出现兼容问题。

内容的提问来源于stack exchange,提问作者Léo Natan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 15:15:34