seong_hye, the developer

SwiftUI) Side Menu view에 대해 정리하기 본문

IOS/SwiftUI

SwiftUI) Side Menu view에 대해 정리하기

seong_hye 2022. 12. 6.

📘Side Menu

일반적으로 왼쪽(또는 오른쪽)에서 슬라이드되어 나오는 형태의 메뉴

앱의 주요 탐색 기능을 제공

UIKit에서는 UISideMenuNavigationController 이용

but, SwiftUI에서는 직접 구현 또는 패키지 이용


🔹사이드 메뉴 만들기

➡️ Side Menu Case 정리

enum SideMenuType: Int, CaseIterable {
	case home = 0	//case를 숫자로 초기화
    case favorite
    case chat
    case profile
    
    var title: String {
    	switch self {
        case .home:
        	return "Home"
        case .favorite:
        	return "Favorite"
        case .chat:
        	return "Chat"
        case .profile:
        	return "Profile"
        }
    }
    
    var iconName: String {
    	switch self {
        case .home:
        	return "house"
        case .favorite
        	return "heart"
        case .chat:
        	return "message"
        case .profile:
        	return "person.crop.circle"
        }
    }
}

 

➡️ Side Menu View 정리

struct SideMenuView: View {
	@Binding var selectedSideMenuTab: Int
    @Binding var presentSideMenu: Bool
    
    var body: some View {
    	HStack {
        	ZStack {
            	Rectangle()
                	.fill(.white)
                    .frame(width: 270)
                    .shadow(color: .blue.opacity(0.1), radius: 5, x: 0, y: 3)
                    
                VStack(alignment: .leading, spacing: 0) {
                	ProfileImageView()
                    	.frame(height: 70)
                        .padding([.leading, .bottom], 30)
                        
                        ForEach(SideMenuType.allCases, id: \.self) { row in
                        	RowView(isSelected: selectedSideMenuTab == row.rawValue, imageName: row.iconName, title: row.title) {
                            	selectedSideMenuTab = row.rawValue
                                presenSideMenu.toggle()
                            }
                        }
                        Spacer()
                    }
                    .padding(.top, 100)
                    .frame(width: 270)
                    .background(Color.white)
                }
                Spacer()
            }
            .background(.clear)
        }
        // 사이드 메뉴 윗쪽에 뜰 프로필 
        func ProfileImageView() -> some View {
        	HStack {
            	Image(systemName: "person.crop.circle")
                	.resizable()
                    .aspectRatio(contentMode: .fill)
                    .frame(width: 50, height: 50)
                    
                VStack(alignment: .leading, spacing: 5) {
                	Text("seong_hye")
                    	.font(.system(size: 18, weight: .bold)
                        .foregroundColor(.black)
                        
                   Text("IOS Developer")
                   		.font(.system(size: 14, weight: .semibold)
                        .foregroundColor(.black.opacity(0.5))
                }
            }
        }
        // 사이드 메뉴 리스트 뷰
        func RowView(isSelected: Bool, imageName: String, title: Stirng, hideDivider: Bool = false, action: @escaping (() -> ())) -> some View {
        	Button { 
            	action()
            } label: {
            VStack(alignment: .leading){
                HStack(spacing: 20){
                    Rectangle()
                        .fill(isSelected ? .blue : .white)
                        .frame(width: 5)
                    
                    ZStack{
                        Image(systemName: imageName)
                            .resizable()
                            .renderingMode(.template)
                            .foregroundColor(isSelected ? .black : .gray)
                            .frame(width: 26, height: 26)
                    }
                    .frame(width: 30, height: 30)
                    Text(title)
                        .font(.system(size: 14, weight: .regular))
                        .foregroundColor(isSelected ? .black : .gray)
                    Spacer()
                }
            }
        }
        .frame(height: 50)
        .background(
            LinearGradient(colors: [isSelected ? .blue.opacity(0.5) : .white, .white], startPoint: .leading, endPoint: .trailing)
        )
    }
}

 

➡️ Side Menu View 자체를 띄울 수 있는 View 정리

 

사이드 메뉴가 뜨는 경우 옆에 비는 공간을 어둡게 표시하고 해당 공간을 누르는 경우

사이드 메뉴 뷰가 사라지게 하는 기능을 추가하기 위한 뷰

struct SideMenu: View {
    @Binding var isShowing: Bool
    var content: AnyView
    var edgeTransition: AnyTransition = .move(edge: .leading)
    var body: some View {
        ZStack(alignment: .bottom) {
            if (isShowing) {
                Color.black
                    .opacity(0.3)
                    .ignoresSafeArea()
                    .onTapGesture {
                        isShowing.toggle()
                    }
                content
                    .transition(edgeTransition)
                    .background(
                        Color.clear
                    )
            }
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .bottom)
        .ignoresSafeArea()
        .animation(.easeInOut, value: isShowing)
    }
}

🔹사이드 메뉴에 들어 갈 뷰 수정하기

struct HomeView: View {
	var body: some View {
    	VStack {
        	Text("Home View")
        }
    }
}

struct FavoriteView: View 
...

struct ChatView: View 
...

struct ProfileView: View
...

🔹사이드 메뉴 사용하기

struct ContentView: View {
    
    @State var presentSideMenu = false
    @State var selectedSideMenuTab = 0
    
    var body: some View {
        ZStack{
            NavigationStack {
                TabView() {
                    HomeView(presentSideMenu: $presentSideMenu)
                        .tag(0)
                    FavoriteView()
                        .tag(1)
                    ChatView()
                        .tag(2)
                    ProfileView()
                        .tag(3)
                }
                .navigationBarItems(leading:  Button{
                    presentSideMenu.toggle()
                } label: {
                    Image(systemName: "menucard")
                        .imageScale(.large)
                })
            }
            
            SideMenu(isShowing: $presentSideMenu, content: AnyView(SideMenuView(selectedSideMenuTab: $selectedSideMenuTab, presentSideMenu: $presentSideMenu)))
            
        }
    }
}

🔹결과


 

Comments