Dev-iOS/UI

[UI] Custom Drawer (Side Menu)- (SwiftUI 2.0 - iOS 14 이상)

lafortune 2023. 7. 21. 09:19
반응형

Drawer (Side Menu)를 커스텀하게 작성하기 위해서는 기본적으로 아래와 같이 작성해서 기본 구조를 잡습니다.

 

struct DrawserWithTabView: View {
	@State private var showDrawer: Bool = false
    var body: some View {
        HStack(spacing: 0) {
            Drawer(showDrawer: $showDrawer)

            TabView {
                Text("Home")
            }
            .frame(width: UIScreen.main.bounds.width)
        }
        .frame(width: UIScreen.main.bounds.width)
        .offset(x: 125)
    }
}


struct Drawer: View {
	@Binding var showDrawer: Bool
    var body: some View {
        VStack {
            HStack {
                Image(systemName: "person")
                    .resizable()
                    .frame(width: 50, height: 50)

                Spacer()
            }
            .padding()

            Spacer()
        }
        .frame(width: 250)
        .background(
            Color.black.opacity(0.5)
                .ignoresSafeArea(.all, edges: .vertical)
        )
    }
}

Drawer (Side Menu)의 너비는 임의로 250으로 설정했습니다. 그리고 Drawer (Side Menu)가 표시될 때의 offset은 너비의 절반을 주었습니다.

 

 

그리고 Drawer (Side Menu)를 보여주고 사라지게 해줄 버튼(DrawerButton)을 만들어줍니다.

struct Drawer: View {
	....

    var body: some View {
        VStack {
            HStack {
                Image(systemName: "person")
                    .resizable()
                    .frame(width: 50, height: 50)

                Spacer()

                DrawerButton(showDrawer: $showDrawer) // 추가
            }
            .padding()

            Spacer()
        }
        ....
    }
}

struct DrawerButton: View {
    @Binding var showDrawer: Bool

    var body: some View {
        Button {
            withAnimation {
                showDrawer.toggle()
            }
        } label: {
            VStack(spacing: 5) {
                Capsule()
                    .fill(Color.white)
                    .frame(width: 35, height: 3)
                    .rotationEffect(.init(degrees: showDrawer ? -50 : 0))
                    .offset(x: showDrawer ? 2 : 0, y: showDrawer ? 9 : 0)

                VStack(spacing: 5) {
                    Capsule()
                        .fill(Color.white)
                        .frame(width: 35, height: 3)

                    Capsule()
                        .fill(Color.white)
                        .frame(width: 35, height: 3)
                        .offset(y: showDrawer ? -8 : 0)
                }
                .rotationEffect(.init(degrees: showDrawer ? 50 : 0))
            }
            .scaleEffect(0.8)
        }

    }
}

 

 

마지막으로 Drawer (Side Menu)가 표시되지 않을 때에는 Drawer Button만 표시되고 Drawer Button를 클릭하게 되면 Drawer (Side Menu)가 표시되면서 Drawer Button 위치를 이동시키도록 코드를 추가 및 변경해줍니다.

 

struct DrawserWithTabView: View {
   ....
    @Namespace var animation

    var body: some View {
        HStack(spacing: 0) {
            Drawer(showDrawer: $showDrawer, animation: animation)

            ....
        }
        .frame(width: UIScreen.main.bounds.width)
        .offset(x: showDrawer ? 125 : -125) 
        .overlay(
            ZStack {
                if !showDrawer {
                    DrawerButton(showDrawer: $showDrawer, animation: animation)
                        .padding()
                }
            }, alignment: .topLeading
        )
    }
}


struct Drawer: View {
    ...
    var animation: Namespace.ID

    var body: some View {
        VStack {
            HStack {
                ...

                if showDrawer {
                    DrawerButton(showDrawer: $showDrawer, animation: animation)
                }
            }
            ...
        }
        ...
    }
}

struct DrawerButton: View {
    ...
    var animation: Namespace.ID

    var body: some View {
        Button {
            ...
        } label: {
            VStack(spacing: 5) {
                Capsule()
                    .fill(showDrawer ? Color.white : Color.primary)
                    ...

                VStack(spacing: 5) {
                    Capsule()
                        .fill(showDrawer ? Color.white : Color.primary)
                        ...

                    Capsule()
                        .fill(showDrawer ? Color.white : Color.primary)
                        ...
                }
                ...
            }
            ...
            .matchedGeometryEffect(id: "DRAWER_BUTTON", in: animation)
        }

    }
}
반응형