- Slides from the left or right edge
- Horizontal drag gesture with axis locking, works alongside
ScrollView,List, andForm - Configurable edge detection zone to avoid conflicts with interactive content
- Tap-to-dismiss overlay with customizable color and opacity
- Matches device display corner radius for a native feel
- All appearance settings exposed as SwiftUI environment modifiers
SlideMenuButtonreads the toggle action from the environment, no bindings needed
dependencies: [
.package(url: "https://github.com/matteozappia/SlideMenu.git", branch: "main")
]import SlideMenuSlideMenu {
Text("Main Content")
} menu: {
VStack {
Text("Home")
Text("Settings")
}
}Place a SlideMenuButton anywhere inside the main content. It picks up the toggle action from the environment automatically.
SlideMenu {
NavigationStack {
Text("Hello")
.toolbar {
ToolbarItem(placement: .topBarLeading) {
SlideMenuButton(systemImage: "sidebar.leading")
}
}
}
} menu: {
Text("Menu")
}Custom label:
SlideMenuButton {
Label("Menu", systemImage: "line.3.horizontal")
}SlideMenu(direction: .right) {
Text("Main")
} menu: {
Text("Right Side Menu")
}Control how far from the screen edge a swipe is recognized. Defaults to 30 points. Increase if users have trouble opening the menu, decrease to reduce conflicts with horizontal controls.
SlideMenu(edgeZoneWidth: 50) {
// ...
} menu: {
// ...
}Use the menu to switch views in the main content area. Instead of putting NavigationLinks in the menu (which would push inside the menu panel), use buttons that set a @State selection, and read slideMenuToggle from the environment to close the menu on tap.
slideMenuToggle is available in both the main and menu content.
enum Page: String, CaseIterable, Identifiable {
case home, settings, about
var id: Self { self }
}
struct MyApp: View {
@State private var selection: Page = .home
var body: some View {
SlideMenu {
NavigationStack {
switch selection {
case .home: HomeView()
case .settings: SettingsView()
case .about: AboutView()
}
}
} menu: {
MenuList(selection: $selection)
}
}
}
struct MenuList: View {
@Binding var selection: Page
@Environment(\.slideMenuToggle) private var toggle
var body: some View {
VStack(alignment: .leading, spacing: 24) {
ForEach(Page.allCases) { page in
Button(page.rawValue.capitalized) {
selection = page
toggle()
}
}
}
.padding(.leading, 24)
.padding(.top, 80)
}
}This keeps the NavigationStack in the main area, so any NavigationLink inside destination views pushes correctly within the main content, not the menu.
SlideMenu(direction: .left, edgeZoneWidth: 40) {
NavigationStack {
ContentView()
.toolbar {
ToolbarItem(placement: .topBarLeading) {
SlideMenuButton(systemImage: "sidebar.leading")
}
}
}
} menu: {
MenuView()
}
.slideMenuWidth(300)
.slideMenuBackground(.ultraThinMaterial)
.slideMenuOverlayOpacity(0.5)
.slideMenuBorderWidth(0)
.slideMenuAnimation(.bouncy(duration: 0.5, extraBounce: 0.15))All appearance is configured through view modifiers. Apply them to the SlideMenu or any ancestor.
.slideMenuWidth(320)Accepts any ShapeStyle, solid colors, materials, gradients:
.slideMenuBackground(Color(.systemBackground))
.slideMenuBackground(.ultraThinMaterial)
.slideMenuBackground(LinearGradient(colors: [.blue, .purple], startPoint: .top, endPoint: .bottom))The dimming overlay drawn on the main content while the menu is open:
.slideMenuOverlayColor(.black)
.slideMenuOverlayOpacity(0.4)The subtle stroke around the main content while the menu is visible:
.slideMenuBorderColor(.gray)
.slideMenuBorderWidth(0.5).slideMenuAnimation(.spring(response: 0.35, dampingFraction: 0.85))
.slideMenuAnimation(.bouncy(duration: 0.5, extraBounce: 0.15))
.slideMenuAnimation(.easeInOut(duration: 0.3))| Parameter | Type | Default | Description |
|---|---|---|---|
direction |
SlideMenuDirection |
.left |
Edge the menu slides from |
edgeZoneWidth |
CGFloat |
30 |
Touch zone width along the screen edge |
| Modifier | Type | Default | Description |
|---|---|---|---|
.slideMenuWidth |
CGFloat |
290 |
Menu panel width |
.slideMenuBackground |
ShapeStyle |
.background |
Background for both panels |
.slideMenuOverlayColor |
Color |
auto | Dimming overlay color |
.slideMenuOverlayOpacity |
CGFloat |
0.6 |
Max overlay opacity |
.slideMenuBorderColor |
Color |
.primary |
Border stroke color |
.slideMenuBorderWidth |
CGFloat |
1 |
Border stroke width |
.slideMenuAnimation |
Animation |
.spring(...) |
Open/close animation |
The Demo/ folder contains a demo app showcasing all features. The demo app requires iOS 16+, but the SlideMenu framework itself supports iOS 15+.
Contributions welcome. Please feel free to submit a Pull Request.
MIT. See LICENSE for details.