[SwiftUI] 사이드 메뉴 만들기

메뉴 호출

프로젝트를 시작하면 다음과 같은 메세지가 나옵니다.

struct ContentView: View {
    var body: some View {
        Text("Hello, world!")
            .padding()
    }
}

메뉴를 여는 버튼을 붙여 봅니다.

struct ContentView: View {
    var body: some View {
        MainView()
    }
}

struct MainView: View {
    var body: some View {
        Button(action: {
            print("Open the side menu")
        }) {
            Text("Show Menu")
        }
    }
}

MainView는 전체 화면이 필요 합니다.

전체 슈퍼 뷰의 높이와 너비를 알기 위해 MainView 를 GeometryReader로 래핑합니다.

struct ContentView: View {
    var body: some View {
        GeometryReader { geometry in
            MainView()
        }
    }
}

화면이 전체를 사용하도록 변경 합니다.

struct ContentView: View {
    var body: some View {
        GeometryReader { geometry in
            MainView()
                .frame(width: geometry.size.width, height: geometry.size.height)
        }
    }
}

메뉴 디자인

이제 사이드 메뉴를 디자인하겠습니다.

새로운 File-New-File을 만들고, SwiftUI 보기를 만들고 그것을 MenuView 라고 부릅니다.
메뉴에는 세로로 배열된 4개의 메뉴 항목이 있어야 합니다.
이를 위해, 우리는 leading 정렬 모드 VStack를 사용 합니다.

struct MenuView: View {
    var body: some View {
        VStack(alignment: .leading) {
            
        }
    }
}

메뉴는 이미지와 텍스트로 만듭니다.

VStack(alignment: .leading) {
            HStack {
                
            }
        }

아이콘을 넣어 줍니다.

이제 "사람"시스템 아이콘을 사용하여 회색으로 만들고 확대합니다.

VStack(alignment: .leading) {
          HStack {
              Image(systemName: "person")
                  .foregroundColor(.gray)
                  .imageScale(.large)
              Text("Profile")
                  .foregroundColor(.gray)
                  .font(.headline)
                  }
        }

3개 만듭니다.

VStack(alignment: .leading) {
            HStack {
                Image(systemName: "person")
                    .foregroundColor(.gray)
                    .imageScale(.large)
                Text("Profile")
                    .foregroundColor(.gray)
                    .font(.headline)
            }
                .padding(.top, 100)
            HStack {
                Image(systemName: "envelope")
                    .foregroundColor(.gray)
                    .imageScale(.large)
                Text("Messages")
                    .foregroundColor(.gray)
                    .font(.headline)
            }
                .padding(.top, 30)
            HStack {
                Image(systemName: "gear")
                    .foregroundColor(.gray)
                    .imageScale(.large)
                Text("Settings")
                    .foregroundColor(.gray)
                    .font(.headline)
            }
                .padding(.top, 30)
        }

마지막에 Spacer 를 사용해 위로 올립니다.

VStack(alignment: .leading) {
            HStack {
                //...
            }
                .padding(.top, 100)
            HStack {
                //...
            }
            .padding(.top, 30)
            HStack {
                //....
            }
            .padding(.top, 30)
            Spacer()
        }

ContentView에 MenuView 추가

MenuView 가 ContentView 내부에 표시되어야 하는지 여부를 추적하기 위해 다음과 같은 State를 선언합니다.

struct ContentView: View {

    @State var showMenu = false

    var body: some View {
        //...
    }
}

showMenu 상태가 true이면 MenuView가 MainView 상단에 표시되어야 하고 왼쪽에 정렬되어 있어야 합니다. 따라서 MainView를 ZStack으로 래핑 하고 State 값에 따라 MenuView를 삽입합니다 .

    var body: some View {
        GeometryReader { geometry in
            ZStack(alignment: .leading) {
                MainView()
                    .frame(width: geometry.size.width, height: geometry.size.height)
                if self.showMenu {
                    MenuView()
                }
            }
        }
    }

메뉴는 화면의 절반만 덮어야 하므로 다음 .frame을 추가합니다 .

if self.showMenu {
    MenuView()
        .frame(width: geometry.size.width/2)
    }

이제 MainView 에서 showMenu State 로 바인딩을 만들 수 있습니다 .

struct MainView: View {
    
    @Binding var showMenu: Bool
    
    var body: some View {
        //...
    }
}

ContentView 내에서 초기화합니다 .

MainView(showMenu: self.$showMenu)
    .frame(width: geometry.size.width, height: geometry.size.height)

이제 MainView의 버튼을 사용 하여 Binding을 통해 showMenu 상태 를 토글할 수 있습니다. 그러면 ContentView 가 결국 MenuView를 표시하여 자체적으로 다시 빌드됩니다.

Button(action: {
            self.showMenu = true
        }) {
            Text("Show Menu")
        }

또한 사이드 메뉴가 열릴 때 MainView 를 오른쪽 으로 이동하려고 합니다 . 또한 메뉴가 다시 닫힐 때까지 MainView 의 모든 기능을 비활성화하려고 합니다.

MainView(showMenu: self.$showMenu)
    .frame(width: geometry.size.width, height: geometry.size.height)
    .offset(x: self.showMenu ? geometry.size.width/2 : 0)
    .disabled(self.showMenu ? true : false)

그러나 지금까지는 메뉴가 애니메이션 없이 계속 표시됩니다. 대신 "슬라이드 인" 전환을 사용하려고 합니다.
따라서 MainView 의 버튼 동작을 withAnimation 문으로 래핑합니다.

Button(action: {
            withAnimation {
               self.showMenu = true
            }
        }) {
            Text("Show Menu")
        }

이제 MenuView에 전환 수정자를 첨부 하고 메뉴가 왼쪽에서 이동하도록 지정할 수 있습니다.

MenuView()
    .frame(width: geometry.size.width/2)
    .transition(.move(edge: .leading))

힌트 : 라이브 미리보기에서 애니메이션이 실행되고 있지 않다면 일반 시뮬레이터에서 앱을 실행하세요.

메뉴를 닫으려면 스와이프하세요

오른쪽에서 왼쪽으로 스와이프하여 메뉴를 다시 닫을 수 있기를 원합니다. 이를 위해 소위 드래그 제스처를 사용합니다. ContentView의 본문 내에서 선언 하고 나머지 보기의 콘텐츠를 return 키워드로 표시하여 이러한 제스처를 만듭니다 .

var body: some View {
        
        let drag = DragGesture()
        
         return GeometryReader { geometry in
            //...
        }
    }

충분히 멀리 스와이프하면 showMenu State를 다시 false 로 설정하여 메뉴를 닫고 싶습니다 . 이를 위해 드래그 제스처에 .onEnded 수정자를 사용합니다 .

let drag = DragGesture()
            .onEnded {
                
        }

여기에서 사용자가 스와이프 제스처로 특정 임계값을 초과했는지 확인합니다. 이 경우 메뉴를 닫습니다.

let drag = DragGesture()
            .onEnded {
                if $0.translation.width < -100 {
                    withAnimation {
                        self.showMenu = false
                    }
                }
            }

이제 생성된 제스처를 ContentView 에 간단히 첨부할 수 있습니다 .

ZStack(alignment: .leading) {
                //...
            }
                .gesture(drag)

버거 버튼 구현 🍔

마지막으로, 우리는 또한 소위 햄버거 아이콘을 사용하여 메뉴를 열고 닫을 수 있는 가능성을 갖고 싶습니다. 이를 위해 ContentView 의 GeometryReader를 NavigationView로 래핑하고 탐색 모음 제목을 추가합니다.

return NavigationView {
            GeometryReader { geometry in
                //...
            }
                .navigationBarTitle("Side Menu", displayMode: .inline)
        }

이제 선행 인수와 함께 .navigationBarItems 수정자를 사용하여 탐색 모음의 왼쪽에 항목을 추가할 수 있습니다 .

.navigationBarTitle("Side Menu", displayMode: .inline)
.navigationBarItems(leading: (
))

이제 적절한 시스템 아이콘을 추가하고 showMenu 상태를 토글하는 버튼으로 래핑합니다

.navigationBarItems(leading: (
                    Button(action: {
                        withAnimation {
                            self.showMenu.toggle()
                        }
                    }) {
                        Image(systemName: "line.horizontal.3")
                            .imageScale(.large)
                    }
                ))

이제 앱을 실행하면 MainView의 버튼과 스와이프 제스처와 탐색 모음의 "햄버거" 아이콘을 사용하여 사이드 메뉴를 열고 닫을 수 있습니다 .

결론

https://blckbirds.com/post/side-menu-hamburger-menu-in-swiftui/
위 강의를 따라해 보았다.

좋은 웹페이지 즐겨찾기