SwiftUI设备旋转后更新tapGesture响应区域的问题
解决方案:修复SwiftUI侧边菜单旋转后点击区域不更新问题
问题根源
- 静态宽度计算:
sideBarWidth使用UIScreen.main.bounds初始化,属于静态值,设备旋转后不会自动更新,导致侧边栏宽度与背景点击区域错位。 - 无效的背景视图布局:嵌套的
GeometryReader { _ in EmptyView() }未绑定当前容器的动态尺寸,旋转后手势响应区域仍停留在旧尺寸范围。 - 手势区域未跟随布局更新:背景视图尺寸未随父容器旋转实时调整,导致
onTapGesture触发区域无法同步适配新界面。
修改步骤与代码实现
1. 替换静态宽度为动态计算
将sideBarWidth改为从GeometryReader的proxy中实时获取当前视图宽度的70%,确保旋转时自动更新。
2. 简化背景视图布局
移除嵌套的GeometryReader,直接使用Color作为背景——Color默认会填充父容器全部空间,旋转时自动适配新尺寸。
3. 优化视图依赖关系
让视图依赖于GeometryReader的proxy尺寸,旋转时proxy变化会自动触发视图重绘,无需手动更新手势区域。
修改后的完整代码:
struct ContentView: View { var viewModel = SettingsSideMenuViewModel() var body: some View { VStack { SideMenu(viewModel: viewModel) Button("Present menu") { viewModel.isShown.toggle() } Spacer() } .padding() } } final class SettingsSideMenuViewModel: ObservableObject { @Published var isShown = false func dismissHostingController() { guard !isShown else { return } DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { debugPrint("viewShoudBeDismissedHere") } } } struct SideMenu: View { @ObservedObject var viewModel: SettingsSideMenuViewModel var body: some View { GeometryReader { proxy in let sideBarWidth = proxy.size.width * 0.7 // 动态计算侧边栏宽度 ZStack { // 简化背景视图,直接用Color填充整个容器 Color.black.opacity(0.6) .opacity(viewModel.isShown ? 1 : 0) .animation(.easeInOut.delay(0.2), value: viewModel.isShown) .onTapGesture { viewModel.isShown.toggle() viewModel.dismissHostingController() } content(sideBarWidth: sideBarWidth) } .edgesIgnoringSafeArea(.all) } } // 将侧边栏宽度作为参数传入,确保实时更新 func content(sideBarWidth: CGFloat) -> some View { HStack(alignment: .top) { ZStack(alignment: .top) { Color.white Text("SOME VIEW HERE") VStack(alignment: .leading, spacing: 20) { Text("SOME VIEW HERE") Divider() Text("SOME VIEW HERE") Divider() Text("SOME VIEW HERE") } .padding(.top, 80) .padding(.horizontal, 40) } .frame(width: sideBarWidth) .offset(x: viewModel.isShown ? 0 : -sideBarWidth) .animation(.default, value: viewModel.isShown) Spacer() } } } struct DeviceRotationViewModifier: ViewModifier { let action: (UIDeviceOrientation) -> Void func body(content: Content) -> some View { content .onAppear() .onReceive(NotificationCenter.default.publisher(for: UIDevice.orientationDidChangeNotification)) { _ in action(UIDevice.current.orientation) } } } extension View { func onRotate(perform action: @escaping (UIDeviceOrientation) -> Void) -> some View { self.modifier(DeviceRotationViewModifier(action: action)) } } struct SideMenu_Previews: PreviewProvider { static var viewModel = SettingsSideMenuViewModel() static var previews: some View { SideMenu(viewModel: viewModel) } }
额外说明
- 若仍需监听设备旋转状态,可保留
onRotate修饰符,但上述修改已通过GeometryReader的动态尺寸实现手势区域自动更新,无需依赖orientation变量。 - 移除了无用的
@State private var orientation变量,简化代码结构。
内容的提问来源于stack exchange,提问作者imnikita
相关产品推荐
相关产品推荐

