0

I have an issue with updating the area(frame) of .onTapGesture after a device is rotated. Basically, even after changing @State var orientation the area where .onTapGesture works remain the same as on the previous orientation. Would appreciate having any advice on how to reset that tap gesture to the new area after rotation. Thanks in advance!

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
    
    @State private var orientation = UIDeviceOrientation.unknown
    
    var sideBarWidth = UIScreen.main.bounds.size.width * 0.7
    
    var body: some View {
        
        GeometryReader { proxy in
            ZStack {
                GeometryReader { _ in
                    EmptyView()
                }
                .background(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
            }
            .edgesIgnoringSafeArea(.all)
            .frame(width: proxy.size.width,
                   height: proxy.size.height)
            .onRotate { newOrientation in
                orientation = newOrientation
            }
        }
    }
    
    var content: 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)
    }
}

In this example is just slideoutMenu with a blurred area. By opening that menu in portrait and taping on the blurred area this menu should close. The issue is when the menu is opened in portrait and then rotated to landscape - the tapGesture area stays the same as it was in portrait, hence if tapped in the landscape - nothing happens. This works in the same direction too. Thus the question is how to reset the tapGesture area on rotation?

imnikita
  • 21
  • 6
  • 1
    To make it easier to answer your question, can you please create a [mcve] – Ashley Mills Jan 18 '23 at 13:16
  • @AshleyMills updated my example above and added some additional explanations. Thanks! – imnikita Jan 18 '23 at 13:37
  • 1
    Thank you, that's better, but the example doesn't compile. It's missing a `ViewModel`, and `isSidebarVisible`. If I remove the `ViewModel` and add a couple of State variables I can get it to compile and run, but nothing happens. The code needs to exhibit the problem you're seeing, so please try it out yourself before posting. The best way to do this is to create a new project in Xcode with just this code so there's no "baggage" – Ashley Mills Jan 18 '23 at 13:49
  • Sorry, should work now. – imnikita Jan 18 '23 at 14:00
  • 1
    I just get a blank screen. Please try and run the code yourself exactly as you've posted it here. – Ashley Mills Jan 18 '23 at 14:19
  • The code is updated, but the issue us also resolved. This view is presented in UIHostingController. slideOutView?.modalPresentationStyle = .custom the issue is there. But if slideOutView?.modalPresentationStyle = .fullScreen (or whatever) - everything is okay. @AshleyMills thank you! Have figured this out when created test app for you to reproduce the issue – imnikita Jan 18 '23 at 15:29

1 Answers1

-1

This view is presented in UIHostingController. slideOutView?.modalPresentationStyle = .custom the issue is there. But if slideOutView?.modalPresentationStyle = .fullScreen (or whatever) - everything works okay.

imnikita
  • 21
  • 6