如何设置环境值,让CustomPresentationDetent读取外部参数?
我想实现一个CustomPresentationDetent,它的高度依赖外部传入的值,比如高度为maxDetentValue减去指定的x值,代码框架如下:
struct MyDetent: CustomPresentationDetent { static func height(in context: Context) -> CGFloat? { context.maxDetentValue - x } }
但CustomPresentationDetent.height(in:)是静态方法,无法编写带参数的初始化器,使用时只能传入元类型(.custom(MyDetent.self)而非.custom(MyDetent(x: 100))),因此考虑通过环境传递值。
官方文档指出height(in:)的context环境来自sheet展示的位置,但我尝试在多个位置添加.environment,height(in:)始终只能读到默认值0,以下是最小复现代码:
struct ContentView: View { var body: some View { SheetPresenter() .environment(\.myValue, 1) } } struct SheetPresenter: View { @State private var shown = false var body: some View { Button("Show Sheet") { shown = true } .environment(\.myValue, 2) .sheet(isPresented: $shown) { Text("Sheet") .environment(\.myValue, 3) .presentationDetents([.custom(MyDetent.self)]) .environment(\.myValue, 4) } .environment(\.myValue, 5) } } struct MyEnvKey: EnvironmentKey { static let defaultValue: Int = 0 } extension EnvironmentValues { var myValue: Int { get { self[MyEnvKey.self] } set { self[MyEnvKey.self] = newValue } } } struct MyDetent: CustomPresentationDetent { static func height(in context: Context) -> CGFloat? { print(context.myValue) return 100 } }
在iOS 17.4模拟器中,print(context.myValue)始终输出0。
核心问题
普通的.environment modifier只能影响视图本身及其子视图,无法触及sheet的presentation环境上下文。CustomPresentationDetent.Context读取的是专门的presentation环境,需要用presentationEnvironment modifier来配置。
正确实现方式
使用.presentationEnvironment(\.myValue, value: 目标值)传递环境值,这个modifier专门用于修改sheet、popover等presentation组件的环境,能被CustomPresentationDetent.Context正确读取。
修改后的SheetPresenter代码:
struct SheetPresenter: View { @State private var shown = false var body: some View { Button("Show Sheet") { shown = true } .sheet(isPresented: $shown) { Text("Sheet") .presentationDetents([.custom(MyDetent.self)]) } // 关键:用presentationEnvironment传递值给detent的context .presentationEnvironment(\.myValue, 5) } }
此时MyDetent的height(in:)方法就能正确读取到5。
动态参数优化
如果需要动态调整传入的x值,可结合@State或@Binding实现:
struct SheetPresenter: View { @State private var shown = false @State private var offset: CGFloat = 100 // 可动态调整的偏移值 var body: some View { Button("Show Sheet") { shown = true } .sheet(isPresented: $shown) { Text("Sheet") .presentationDetents([.custom(MyDetent.self)]) } .presentationEnvironment(\.myValue, Int(offset)) // 添加滑块动态修改偏移值 Slider(value: $offset, in: 0...200) .padding() } }
原理说明
presentationEnvironment是iOS 16+引入的modifier,专门用于配置presentation组件的环境参数,这些参数会被注入到CustomPresentationDetent.Context中,而普通.environment无法触达这个层级。
内容的提问来源于stack exchange,提问作者Sweeper

