SwiftUI로 사이드 메뉴 구현

SwiftUI로 사이드 메뉴를 재현해 보았습니다.
드래그 또는 탭으로 표시하거나 숨길 수 있습니다.

환경


  • xcode12.1
  • Swift 5



  • 이미지



    핑크 메뉴
    파랑 홈
    검은 테두리가 보이는 곳


    드래그에 맞게 메뉴와 홈 오프셋을 모두 이동합니다.

    코드 전문


    
    import SwiftUI
    
    struct HomeView: View {
    
        @State var showMenu: Bool = false
    
        let menuWidth = UIScreen.main.bounds.width * 0.7
    
        /// homeのx座標(青いやつ)
        @State var xPosition: CGFloat = 0
        @State var isDrag: Bool = false
    
        var drag: some Gesture {
            DragGesture()
                .onChanged{ value in
                    isDrag = true
    
                    // value.location.x - value.startLocation.x がドラッグしている距離
                    // 移動させすぎないようにmaxとminで最大値と最小値の設定
                    if showMenu {
                        xPosition = max(min(menuWidth + value.location.x - value.startLocation.x, menuWidth), 0)
                    } else {
                        xPosition = max(min(value.location.x - value.startLocation.x, menuWidth), 0)
                    }
                }
                .onEnded{ value in
    
                    // Dragが終了したタイミングで開くか、閉じるかを判定したい
                    if value.location.x - value.startLocation.x >= menuWidth / 3 {
                        showMenu = true
                    } else if -(value.location.x - value.startLocation.x) >= menuWidth / 3 {
                        showMenu = false
                    }
                    isDrag = false
                }
        }
    
        var body: some View {
    
            GeometryReader { geometry in
                VStack {
                    HStack(spacing: 60) {
                        AvatarView(showMenu: $showMenu)
                        Spacer()
                    }
                    .padding(EdgeInsets.init(top: 8, leading: 8, bottom: 8, trailing: 8))
                    Spacer()
                }
                .frame(width: geometry.size.width, height: geometry.size.height)
                .offset(x: isDrag ? xPosition : (showMenu ? menuWidth : 0))
                .animation(.easeInOut(duration: 0.2))
                .background(Color.blue)
                .onTapGesture {
                    if showMenu {
                        showMenu.toggle()
                    }
                }
                .gesture(drag)
    
                SlideMenuView(showMenu: $showMenu)
                    .frame(width: menuWidth, height: geometry.size.height)
                    .offset(x: isDrag ? -menuWidth + xPosition : (showMenu ? 0 : -menuWidth))
                    .animation(.easeInOut(duration: 0.2))
                    .gesture(drag)
    
            }
        }
    }
    
    struct AvatarView: View {
        @Binding var showMenu: Bool
    
        var body: some View {
            Button(action: {
                self.showMenu.toggle()
    
            }) {
                Color.yellow
                    .frame(width: 44, height: 44)
                    .clipShape(Circle())
            }
        }
    }
    
    
    struct SlideMenuView: View {
    
        @Binding var showMenu: Bool
    
        var body: some View {
            VStack(spacing: 16) {
                Spacer()
                MenuRow(showMenu: $showMenu, title: "Account", icon: "gear")
                MenuRow(showMenu: $showMenu, title: "Billing", icon: "creditcard")
                MenuRow(showMenu: $showMenu, title: "Sign out", icon: "person.crop.circle")
                Spacer()
    
            }
            .background(Color.white)
        }
    }
    
    struct MenuRow: View {
        @Binding var showMenu: Bool
    
        var title: String
        var icon: String
    
        var body: some View {
    
            Button(action: {
                self.showMenu.toggle()
    
            }) {
                HStack(spacing: 16) {
    
                    Image(systemName: icon)
                        .font(.system(size: 20, weight: .light))
                        .imageScale(.large)
                        .frame(width: 32, height: 32)
    
                    Text(title)
                        .font(.system(size: 20, weight: .bold, design: .default))
                        .frame(width: 120, alignment: .leading)
                    Spacer()
                }
                .padding(.horizontal, 30)
    
            }
        }
    }
    
    
    
    struct ContentView_Previews: PreviewProvider {
        static var previews: some View {
            HomeView()
        }
    }
    

    감상



    좀 더 깨끗한 구현이 있었을지도. . .
    그렇다고해도 SwiftUI는 쓰기 쉽네요.

    좋은 웹페이지 즐겨찾기