๐๐ป Drawer Menu๋ฅผ swift์์ ๊ตฌํํ๋ ๋ฐฉ๋ฒ์
๋๋ค
How to implement a Drawer Menu in Swift
๐๐ป Drawer Menu์ ํต์ฌ์ ์ ๋๋ฉ์ด์
์์ฑ์ ์ฌ์ฉํด์ ๋ฉ๋ด ์์น๋ฅผ ์ด๋ํ๋ ๊ฒ์
๋๋ค.
The key to the Drawer Menu is using animation properties to move the menu position.
โ๏ธContentView.swift
import SwiftUI
struct ContentView: View {
@State private var showMenu = false
@State private var selectedMenu = "Home"
@State private var hideStatusBar = false
var body: some View {
ZStack {
// Main content
NavigationView {
VStack {
switch selectedMenu {
case "Home":
HomeScreen()
// HomeScreen2()
case "Search":
SearchScreen()
// SearchScreen2()
case "Settings":
SettingsScreen()
// SettingsScreen2()
default:
HomeScreen()
}
}
.navigationBarTitle(Text(selectedMenu), displayMode: .inline)
.navigationBarItems(leading: Button(action: {
withAnimation(.easeInOut(duration: 0.3)) {
showMenu.toggle()
}
}) {
Image(systemName: "line.3.horizontal")
.imageScale(.large)
})
}
.disabled(showMenu)
// ๋ฐํฌ๋ช
๋ฐฐ๊ฒฝ (ํญ์ ์กด์ฌ + opacity๋ก ์ ์ด)
// Semi-transparent background (always present + controlled by opacity)
Color.black
.opacity(showMenu ? 0.4 : 0.0)
.animation(.easeInOut(duration: 0.3), value: showMenu)
.edgesIgnoringSafeArea(.all)
.ignoresSafeArea() // Status Bar ํฌํจ
.edgesIgnoringSafeArea(.all) // Status Bar ํฌํจ(ios15์ด์ ์ฉ)
.onTapGesture {
if showMenu {
withAnimation(.easeInOut(duration: 0.3)) {
showMenu = false
}
}
}
// Drawer (offset์ผ๋ก ์ฌ๋ผ์ด๋ ์ธ/์์)
// Drawer (slides in/out with offset)
// Drawer๋ Dynamick Island๋ ๊ฐ๋ฆด ์ ์์(์๊ณ ์ฐ์ธก ํ์)
// Drawer cannot cover Dynamick Island (oval on the right side of the clock)
HStack {
// ํ๋ฉด์์ ๋ณด ๊ณ์ฐํ ๋ GeometryReader์ฌ์ฉ
// Use GeometryReader when calculating screen information
GeometryReader { geo in
VStack(alignment: .leading) {
Text("Menu Title")
.font(.title)
.padding(.top, 50)
.padding(.bottom, 20)
// ๊ฐ ๋ฉ๋ด / Each Menu
DrawerMenuItem(title: "Home", selectedMenu: $selectedMenu, showMenu: $showMenu)
DrawerMenuItem(title: "Search", selectedMenu: $selectedMenu, showMenu: $showMenu)
DrawerMenuItem(title: "Settings", selectedMenu: $selectedMenu, showMenu: $showMenu)
Spacer() // ๋๋จธ์ง ๊ณต๊ฐ ์ฐจ์ง / Takes up the rest of the space
} // VStack
//.frame(width: 250, height: UIScreen.main.bounds.height) // ํ๋ฉด์ ์ฒด ๋์ด๋ก ์ค์ / Set to full screen height(deprecated)
.frame(width:250, height:geo.size.height) // ๋๋ก์ด๋ฅผ ์ ์ฒด ๋์ด๋ก์ค์ ,geometry reader์ฌ์ฉ / Set the drawer to full height, using the geometry reader
.padding(.horizontal, 16)
.background(Color(UIColor.systemBackground))
}
Spacer()
} // HStack(Drawer)
.offset(x: showMenu ? 0 : -260) // ์คํ์์น 0์ ํ๋ฉด ์ -260ํ๋ฉด ๋ฐ / Stack position 0 is inside the screen -260 is outside the screen
.animation(.easeInOut(duration: 0.3), value: showMenu) // ์๋๋ฉ์ด์
์ ์ฉ / Apply animation
/*
animation์ ์ง์ ํ์ง์๊ณ offset๋ง ์ง์ ํด๋ ios์์๋ ์๋ฌต์ ์ผ๋ก ์ ๋๋ฉ์ด์
์ฒ๋ฆฌํจ
animation์ ์์ผ๋ฉด ๋ ์์ธํ ์ค์ ์ด ๊ฐ๋ฅํจ.
Even if you only specify an offset without specifying animation, iOS implicitly handles animation.
Animation allows for more detailed settings.
*/
}
.statusBar(hidden: hideStatusBar) // Drawer์ด๋ฆด ๊ฒฝ์ฐ Statusbar์จ๊น / Hide Statusbar when Drawer is opened
.onChange(of: showMenu) { oldValue, newValue in
hideStatusBar = newValue
}
//zstack
} // body
} //ContentView
// MARK: - DrawerMenuItem
struct DrawerMenuItem: View {
let title: String //Home,Search,Settings
@Binding var selectedMenu: String // Default value "Home"
@Binding var showMenu: Bool // true,false
/**
@Binding : ๋ถ๋ชจ๋ทฐ์ ์ํ(@State)๋ฅผ ์์๋ทฐ๊ฐ ์์ ํ ์ ์๊ฒ ์ฐ๊ฒฐํด์ฃผ๋ ์ฐธ์กฐ ,c++์ ํฌ์ธํฐ์ ๊ฐ์ ๊ฐ๋
*/
var body: some View {
Button(action: {
withAnimation(.easeInOut(duration: 0.3)) {
// ๋ฉ๋ดํ์ดํ ๋ณ๊ฒฝ(ํ๋ผ๋ฉํฐ๋ก ์
๋ ฅ๋ ๊ฐ์ ๋์
) / Change menu title (substitute the value entered as a parameter)
selectedMenu = title
showMenu = false // Close Drawer
}
}) {
HStack {
Text(title) // Home,Search,Settings
.font(.headline)
.foregroundColor(selectedMenu == title ? .blue : .primary)
Spacer()
}
.padding(.vertical, 10)
}
}
}
// MARK: - Screens(๋ก์ปฌ/local)
struct HomeScreen: View {
var body: some View {
Text("๋ก์ปฌํ๋ฉด/Local screen.")
Text("ํ ํ๋ฉด์
๋๋ค./Home Screen").font(.title).padding()
}
}
struct SearchScreen: View {
var body: some View {
Text("๋ก์ปฌํ๋ฉด./ Local Screen")
Text("๊ฒ์ ํ๋ฉด์
๋๋ค. / Search Screen").font(.title).padding()
}
}
struct SettingsScreen: View {
var body: some View {
Text("๋ก์ปฌํ๋ฉด. / Local Screen")
Text("์ค์ ํ๋ฉด์
๋๋ค. / Settings Screen").font(.title).padding()
}
}
// MARK: - Preview
#Preview {
ContentView()
}
โ๏ธScreenShot

