3
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

SwiftUIでサイドメニューの実装

Last updated at Posted at 2020-10-31

SwiftUIでサイドメニューを再現してみました。
ドラッグやタップで表示、非表示にできます。

環境

  • xcode12.1
  • Swift 5

slidemenu.gif

イメージ

ピンク  メニュー
青   ホーム
黒枠  見えているところ

ドラッグに合わせてメニューとホームの両方のオフセットをずらしています。

コード全文


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って書きやすいですね。

3
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?