SwiftUI子视图内容无法随旋转自适应,仅主视图直接写入正常
问题原因与解决方法
问题原因
- 全局函数无刷新触发机制:你的
getFrame()是全局函数,它的计算结果仅在MockView首次初始化时执行一次。SwiftUI不会自动监听UIScreen.main.bounds的变化,因此设备旋转时不会重新调用该函数更新Text的尺寸。 - 子视图未感知布局变化:当代码直接放在
MainView的GeometryReader中时,GeometryReader的闭包会在布局变化(如设备旋转)时重新执行,从而重新计算尺寸;但MockView作为独立子视图,没有绑定到任何能触发重绘的状态,无法感知布局变化。 - 原生视图与硬编码的差异:
Rectangle是SwiftUI原生视图,会自动响应父布局的变化;而Text的尺寸是硬编码了首次计算的getFrame()结果,没有更新逻辑。
解决方法
方法一:利用环境尺寸类监听横竖屏
通过horizontalSizeClass环境变量,它会在设备旋转时自动更新,触发视图重绘:
struct MockView : View { @Environment(\.horizontalSizeClass) private var horizontalSizeClass private func getFrame() -> (width: CGFloat, height: CGFloat) { let isPortrait = horizontalSizeClass == .regular let height: CGFloat = isPortrait ? 5 : 10 let width: CGFloat = isPortrait ? 3 : 8 return (width, height) } var body: some View { Button { print("ok") } label: { ZStack { Rectangle() .fill(.red) Text("Hello") .frame(width: getFrame().width, height: getFrame().height) .background(Color.gray) .cornerRadius(20) } } } }
方法二:在MockView内部嵌入GeometryReader
让子视图自身感知父布局的变化,旋转时自动重新计算尺寸:
struct MockView : View { var body: some View { GeometryReader { geometry in Button { print("ok") } label: { ZStack { Rectangle() .fill(.red) Text("Hello") .frame(width: getFrame(for: geometry).width, height: getFrame(for: geometry).height) .background(Color.gray) .cornerRadius(20) } } } } private func getFrame(for geometry: GeometryProxy) -> (width: CGFloat, height: CGFloat) { let isPortrait = geometry.size.width < geometry.size.height let height: CGFloat = isPortrait ? 5 : 10 let width: CGFloat = isPortrait ? 3 : 8 return (width, height) } }
方法三:监听屏幕边界变化(ObservableObject)
如果需要直接基于UIScreen的bounds,可通过ObservableObject监听系统通知:
class ScreenBoundsObserver: ObservableObject { @Published var bounds = UIScreen.main.bounds init() { NotificationCenter.default.addObserver( self, selector: #selector(updateBounds), name: UIScreen.boundsDidChangeNotification, object: nil ) } @objc private func updateBounds() { bounds = UIScreen.main.bounds } deinit { NotificationCenter.default.removeObserver(self) } } struct MockView : View { @StateObject private var screenObserver = ScreenBoundsObserver() private func getFrame() -> (width: CGFloat, height: CGFloat) { let isPortrait = screenObserver.bounds.width < screenObserver.bounds.height let height: CGFloat = isPortrait ? 5 : 10 let width: CGFloat = isPortrait ? 3 : 8 return (width, height) } var body: some View { Button { print("ok") } label: { ZStack { Rectangle() .fill(.red) Text("Hello") .frame(width: getFrame().width, height: getFrame().height) .background(Color.gray) .cornerRadius(20) } } } }
内容的提问来源于stack exchange,提问作者crost
相关产品推荐
相关产品推荐

