diff --git a/Shared/Views/Tab/MainView.swift b/Shared/Views/Tab/MainView.swift index 9d4e668..824ca78 100644 --- a/Shared/Views/Tab/MainView.swift +++ b/Shared/Views/Tab/MainView.swift @@ -10,8 +10,9 @@ struct MainView: View { @State private var accounts = ["@user1", "@user2", "@user3"] @State private var sheetType: ProfileTab.SheetType? = nil - // Состояние для бокового меню + // Состояния для бокового меню @State private var isSideMenuPresented = false + @State private var menuOffset: CGFloat = 0 private var tabTitle: String { switch selectedTab { @@ -22,6 +23,10 @@ struct MainView: View { default: return "Home" } } + + private var menuWidth: CGFloat { + UIScreen.main.bounds.width * 0.8 + } var body: some View { NavigationView { @@ -61,28 +66,69 @@ struct MainView: View { .sheet(item: $sheetType) { type in // ... sheet presentation logic } - // Затемнение фона при открытом меню - .background(isSideMenuPresented ? Color.black.opacity(0.4) : Color.clear) - .onTapGesture { - if isSideMenuPresented { - withAnimation { - isSideMenuPresented = false + + // Затемнение и закрытие по тапу + if isSideMenuPresented { + Color.black.opacity(0.4 + Double((menuOffset / menuWidth))) + .ignoresSafeArea() + .onTapGesture { + withAnimation(.easeInOut) { + isSideMenuPresented = false + } } - } } // Боковое меню - if isSideMenuPresented { - HStack { - SideMenuView(isPresented: $isSideMenuPresented) - .frame(width: UIScreen.main.bounds.width * 0.8) - Spacer() - } - .ignoresSafeArea(edges: .vertical) // Игнорируем safe area - .transition(.move(edge: .leading)) - .zIndex(1) // Убедимся, что меню поверх всего - } + SideMenuView(isPresented: $isSideMenuPresented) + .frame(width: menuWidth) + .offset(x: (isSideMenuPresented ? 0 : -menuWidth) + menuOffset) + .ignoresSafeArea(edges: .vertical) + .zIndex(1) } + .gesture( + DragGesture() + .onChanged { gesture in + // Разрешаем открывать только свайпом от левого края, а закрывать — откуда угодно + if !isSideMenuPresented && gesture.startLocation.x > 60 { + return + } + + let translation = gesture.translation.width + + if isSideMenuPresented { + // Если меню открыто, позволяем двигать его влево до полного закрытия + let newOffset = max(-menuWidth, translation) + self.menuOffset = min(0, newOffset) + } else { + // Если меню закрыто, позволяем двигать его вправо до полного открытия + self.menuOffset = max(0, translation) + } + } + .onEnded { gesture in + // Если свайп был не от края (для закрытого меню), ничего не делаем + if !isSideMenuPresented && gesture.startLocation.x > 60 { + return + } + + let threshold = menuWidth * 0.4 + + withAnimation(.easeInOut) { + if isSideMenuPresented { + // Если меню было открыто и сдвинуто влево больше, чем на `threshold` + if menuOffset < -threshold { + isSideMenuPresented = false + } + } else { + // Если меню было закрыто и сдвинуто вправо больше, чем на `threshold` + if menuOffset > threshold { + isSideMenuPresented = true + } + } + // Возвращаем временное смещение в 0, основное состояние `isSideMenuPresented` сделает остальное + self.menuOffset = 0 + } + } + ) } .navigationViewStyle(StackNavigationViewStyle()) }