SwiftUI implements Side Menu

The effect

code

There are comments in the code

Source making links: gist.github.com/RandyWei/05…


//
// ContentView.swift
// SiderMenuDemo01
//
// Created by RandyWei on 2021/9/7.
//

import SwiftUI

struct ContentView: View {
   
   // Scratch offset
   @GestureState var offset:CGFloat = 0
   
   // The slide should stay at a certain point
   // Stop point: 3/5 of the screen width
   let maxOffset:CGFloat = UIScreen.main.bounds.width * 3 / 5
   
   // Offset after sliding expansion
   @State var expandOffset:CGFloat = 0
   
   // Rebound point: maximum stop point /2
   private var springOffset:CGFloat{
       maxOffset / 2
   }
   // Scale, default is 1
   @State private var scaleRatio:CGFloat = 1
   
   // Minimum scalable value
   let minScale:CGFloat = 0.9
   
   
   private var dragGesture: some Gesture {
       DragGesture()
           .updating($offset, body: { value, out, _ in
               // Determine whether to reverse slide. If it is expanded, reverse slide is required
               if value.translation.width > = 0 || expandOffset ! = 0 {
                   out = value.translation.width
               }
           })
           .onChanged { value in
               // Add transition to zoom for smoothness
               if value.translation.width > = 0 {
                   // Calculate the scaling ratio: scaling value = scaling ratio * scaling value (1-minscale)
                   // Since it is scaled down, it is 1- scaled
                   scaleRatio = 1 - (value.translation.width / maxOffset) * (1 - minScale)
               } else {
                   // Reverse value.translation.width is negative, so +maxOffset becomes positive
                   scaleRatio = 1 - ((maxOffset + value.translation.width) / maxOffset) * (1 - minScale)
               }
           }
           .onEnded { value in
               // It is necessary to determine whether the slide exceeds a certain point to determine whether to reset or stay
               if value.translation.width > = springOffset {
                   expandOffset = maxOffset
                   // When stopped, shrink to 0.9
                   scaleRatio = minScale
               } else {
                   expandOffset = 0
                   scaleRatio = 1}}}var body: some View {
       
       ZStack{
           
           // Side menu layer
           SideMenuView(a)// Function area
           FeatureView()
               .offset(x: offset + expandOffset)
               .scaleEffect(scaleRatio)
               .animation(.easeInOut(duration: 0.05))
               .gesture(dragGesture)
               
           
       }
       
   }
}

struct FeatureView:View {
   
   var body: some View{
       
       GeometryReader{proxy in
           VStack{
               HStack{
                   Image(systemName: "list.dash")
                       .resizable()
                       .frame(width: 20, height: 20, alignment: .center)
                   
                   Text("Functional area")
                       .font(.title)
                   
                   Spacer()}ScrollView(.vertical, showsIndicators: false, content: {
                   
                   VStack{
                       
                       ForEach(0..<50) {_ in
                           
                           HStack{
                               
                               Image(systemName: "person")
                                   .resizable()
                                   .frame(width: 80, height: 80, alignment: .center)
                               
                               VStack(alignment: .leading){
                                   Text("titletitletitletitletitle")
                                       .font(.title)
                                   
                                   Spacer(a)Text("bodybodybodybodybodybody")
                                       .font(.body)
                               }
                               
                           }
                           
                       }.redacted(reason: .placeholder)
                   }
                   
               })
           }
           .padding(.horizontal)
           .padding(.top, 8 + proxy.safeAreaInsets.top)
           .frame(maxWidth:.infinity,maxHeight: .infinity,alignment: .topLeading)
           .background(Color.white)
           .cornerRadius(30)
           .shadow(radius: 10)
           .ignoresSafeArea()
       }
       
   }
}

struct SideMenuView:View {
   var body: some View{
       
       GeometryReader{proxy in
           VStack(alignment:.leading){
               // Family portrait
               Image("avatar")
                   .resizable()
                   .aspectRatio(contentMode: .fill)
                   .frame(width: 100, height: 100, alignment: .center)
                   .clipShape(Circle())
               
               Text("Sir William.")
                   .font(.title)
               
               Text("This man is lazy and has nothing left.")
               
               / / the menu
               
               HStack{
                   Image(systemName: "archivebox")
                   Text("Menu one")
               }
               .padding(.top)
               
               HStack{
                   Image(systemName: "note.text")
                   Text("Menu two")
               }
               .padding(.top)
               
               
               HStack{
                   Image(systemName: "gearshape")
                   Text("Personal Settings")
               }
               .padding(.top)
               
               Spacer(a)HStack{
                   Image(systemName: "signature")
                   Text("Log out")
               }
               .padding(.top)
               
           }
           .foregroundColor(.white)
           .padding(.horizontal)
           .padding(.top, 8 + proxy.safeAreaInsets.top)
           .padding(.bottom, 8 + proxy.safeAreaInsets.bottom)
           .frame(maxWidth:.infinity,maxHeight: .infinity,alignment: .topLeading)
           .background(Color.orange)
           .ignoresSafeArea()
       }
       
   }
}

struct ContentView_Previews: PreviewProvider {
   static var previews: some View {
       ContentView()}}Copy the code

Related video

Swift UI Side Menu- Bilibilii