SwiftUI自定义子视图对齐问题:适配横竖屏相机预览
SwiftUI相机预览适配与对齐问题解决方案
需求说明
- 横屏模式下显示16:9比例的相机预览(用
Color.blue模拟),竖屏模式下显示9:16比例 - 相机预览需与安全区前边缘对齐(可微调偏移)
- 底部按钮覆盖层需与相机预览的边缘严格对齐
原代码问题
顶层Color.clear通过onGeometryChange获取的尺寸在横竖屏切换时出现转置错误,导致手动计算的偏移量失效,同时覆盖层未与相机预览绑定对齐。
修正后的代码
struct CameraUI: View { @Environment(\.verticalSizeClass) var verticalSizeClass @State private var safeAreaInsets: EdgeInsets = .zero private var previewAspectRatio: CGFloat { verticalSizeClass == .regular ? 9/16 : 16/9 } var body: some View { GeometryReader { fullScreenProxy in ZStack(alignment: .leading) { // 相机预览层 Color.blue .aspectRatio(previewAspectRatio, contentMode: .fit) .frame(width: fullScreenProxy.size.width) .padding(.leading, safeAreaInsets.leading) .ignoresSafeArea(edges: [.top, .bottom, .trailing]) // 对齐的按钮覆盖层 VStack { Spacer() HStack { Button("Button1") {} Spacer() Button("Button2") {} } .padding(.horizontal, safeAreaInsets.leading + 16) // 可微调内边距 .padding(.bottom, safeAreaInsets.bottom + 16) } .frame(width: fullScreenProxy.size.width) } } .persistentSystemOverlays(.hidden) .onAppear { // 获取安全区边距 if let window = UIApplication.shared.connectedScenes .compactMap({ $0 as? UIWindowScene }) .first?.windows.first { safeAreaInsets = window.safeAreaInsets } } .onRotate { newOrientation in // 横竖屏切换时重新获取安全区 if let window = UIApplication.shared.connectedScenes .compactMap({ $0 as? UIWindowScene }) .first?.windows.first { safeAreaInsets = window.safeAreaInsets } } } } // 用于监听屏幕旋转的扩展 extension View { func onRotate(perform action: @escaping (UIDeviceOrientation) -> Void) -> some View { self.modifier(RotationModifier(action: action)) } } struct RotationModifier: ViewModifier { let action: (UIDeviceOrientation) -> Void @State private var orientation = UIDevice.current.orientation func body(content: Content) -> some View { content .onAppear { UIDevice.current.beginGeneratingDeviceOrientationNotifications() NotificationCenter.default.addObserver( forName: UIDevice.orientationDidChangeNotification, object: nil, queue: .main ) { _ in orientation = UIDevice.current.orientation action(orientation) } } .onDisappear { UIDevice.current.endGeneratingDeviceOrientationNotifications() } } } #Preview { CameraUI() }
关键改动说明
- 用
GeometryReader替代onGeometryChange:直接获取全屏尺寸,避免横竖屏切换时的尺寸转置问题,确保比例计算准确。 - 安全区对齐处理:通过
onAppear和onRotate监听安全区边距,用padding(.leading)让相机预览与安全区前边缘对齐,可根据需求微调数值。 - 覆盖层绑定对齐:将按钮覆盖层与相机预览放在同一个
ZStack内,使用相同的frame和安全区边距计算,确保两者边缘完全匹配。 - 屏幕旋转监听:添加
onRotate扩展,横竖屏切换时重新获取安全区边距,保证适配实时生效。
内容的提问来源于stack exchange,提问作者Deepak Sharma
相关产品推荐
相关产品推荐

