SwiftUI中如何获取ZStack内弹窗视图的高度?
问题原因分析
你的代码里有两个关键问题,导致拿到的高度不对:
- GeometryReader的布局特性:GeometryReader会自动填满父视图的可用空间,所以你获取的
geometry.size.height其实是ZStack(也就是屏幕)的高度,根本不是弹窗本身的高度。 - 坐标系逻辑搞反:你把
coordinateSpace(name: "popup")加在弹窗上,再用GeometryReader的frame去转换到这个坐标系,这相当于拿大容器在小视图坐标系里的尺寸,完全颠倒了测量逻辑。
解决方案
下面给两种靠谱的方式获取弹窗实际高度:
方法一:用Background嵌入GeometryReader
把GeometryReader作为弹窗的背景,它会自动贴合弹窗的尺寸,直接拿到真实大小:
import SwiftUI struct ContentView: View { @State private var popupHeight: CGFloat = 0 var body: some View { ZStack(alignment: .top) { TestPopupView(message: "Popup height is \(popupHeight)") .background { GeometryReader { geometry in Color.clear .onAppear { popupHeight = geometry.size.height } .onChange(of: geometry.size.height) { newHeight in popupHeight = newHeight } } } } .padding() } } #Preview { ContentView() } struct TestPopupView: View { var message = "Test popup" var body: some View { VStack(spacing: 0) { VStack { Text(message) .multilineTextAlignment(.center) .foregroundColor(.white) .padding() } .background(Color.black) .clipShape(RoundedRectangle(cornerRadius: 10)) } .frame(maxWidth: 235) .frame(minHeight: 90) } }
方法二:用PreferenceKey传递尺寸(更灵活)
如果需要把尺寸传递给多层父视图,用PreferenceKey是更合适的方案:
import SwiftUI // 定义用于传递尺寸的PreferenceKey struct ViewSizeKey: PreferenceKey { static var defaultValue: CGSize = .zero static func reduce(value: inout CGSize, nextValue: () -> CGSize) { value = nextValue() } } struct ContentView: View { @State private var popupHeight: CGFloat = 0 var body: some View { ZStack(alignment: .top) { TestPopupView(message: "Popup height is \(popupHeight)") .onPreferenceChange(ViewSizeKey.self) { size in popupHeight = size.height } } .padding() } } #Preview { ContentView() } struct TestPopupView: View { var message = "Test popup" var body: some View { VStack(spacing: 0) { VStack { Text(message) .multilineTextAlignment(.center) .foregroundColor(.white) .padding() } .background(Color.black) .clipShape(RoundedRectangle(cornerRadius: 10)) } .frame(maxWidth: 235) .frame(minHeight: 90) .background { GeometryReader { geometry in Color.clear .preference(key: ViewSizeKey.self, value: geometry.size) } } } }
说明
两种方法的核心都是让GeometryReader贴合弹窗的实际布局范围,而不是让它占满父视图空间。方法一更简单直接,适合只在弹窗附近用尺寸的场景;方法二更灵活,适合跨层级传递尺寸的需求。
内容的提问来源于stack exchange,提问作者Francis F
相关产品推荐
相关产品推荐

