SwiftUI 實現(xiàn)側(cè)滑菜單 Side Menu

SwiftUI 實現(xiàn)側(cè)滑菜單 Side Menu

效果

iShot2021-09-08 09.43.45.gif

代碼

代碼里都有相關(guān)注釋

源碼 github 鏈接:https://gist.github.com/RandyWei/05261947fcf7493d9edd4b58bdb43cbe


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

import SwiftUI

struct ContentView: View {
   
   //劃動偏移量
   @GestureState var offset:CGFloat = 0
   
   //滑動應(yīng)該停留在某個點
   //停留點: 屏幕寬度的3/5
   let maxOffset:CGFloat = UIScreen.main.bounds.width * 3 / 5
   
   //滑動展開之后的 offset
   @State var expandOffset:CGFloat = 0
   
   //回彈點:最大停留點/2
   private var springOffset:CGFloat{
       maxOffset / 2
   }
   //縮放比例熄捍,默認(rèn)是1
   @State private var scaleRatio:CGFloat = 1
   
   //最小 可縮放值
   let minScale:CGFloat = 0.9
   
   
   private var dragGesture: some Gesture {
       DragGesture()
           .updating($offset, body: { value, out, _ in
               //判斷是否反向滑動,如果是展開狀態(tài)需要反向滑動
               if value.translation.width >= 0 || expandOffset != 0 {
                   out = value.translation.width
               }
           })
           .onChanged { value in
               //為了順暢給縮放增加過渡
               if value.translation.width >= 0 {
                   //對縮放比例進行計算:縮放值 = 劃動比例 * 可縮放值(1-minScale)
                   //因為是往小了縮喂很,所以是1-縮放值
                   scaleRatio = 1 - (value.translation.width / maxOffset) * (1 - minScale)
               } else {
                   //反向value.translation.width是負(fù)數(shù) 她紫,所以+maxOffset變?yōu)檎?                   scaleRatio = 1 - ((maxOffset + value.translation.width) / maxOffset) * (1 - minScale)
               }
           }
           .onEnded { value in
               //需要判斷滑動是否超過某個點來決定是重置還是停留
               if value.translation.width >= springOffset {
                   expandOffset = maxOffset
                   //停止后奈虾,縮小 到0.9
                   scaleRatio = minScale
               } else {
                   expandOffset = 0
                   scaleRatio = 1
               }
           }
   }
   
   var body: some View {
       
       ZStack{
           
           //側(cè)邊菜單層
           SideMenuView()
           
           //功能區(qū)域
           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("功能區(qū)域")
                       .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()
                                   
                                   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){
               //祖?zhèn)黝^像
               Image("avatar")
                   .resizable()
                   .aspectRatio(contentMode: .fill)
                   .frame(width: 100, height: 100, alignment: .center)
                   .clipShape(Circle())
               
               Text("韋爵爺")
                   .font(.title)
               
               Text("這個人很懶,什么都沒留下")
               
               //菜單
               
               HStack{
                   Image(systemName: "archivebox")
                   Text("菜單一")
               }
               .padding(.top)
               
               HStack{
                   Image(systemName: "note.text")
                   Text("菜單二")
               }
               .padding(.top)
               
               
               HStack{
                   Image(systemName: "gearshape")
                   Text("個人設(shè)置")
               }
               .padding(.top)
               
               Spacer()
               
               HStack{
                   Image(systemName: "signature")
                   Text("退出登錄")
               }
               .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()
   }
}

相關(guān)視頻

Swift UI側(cè)滑菜單Side Menu-嗶哩嗶哩

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末疫蔓,一起剝皮案震驚了整個濱河市萧吠,隨后出現(xiàn)的幾起案子豹悬,更是在濱河造成了極大的恐慌,老刑警劉巖分瘦,帶你破解...
    沈念sama閱讀 217,657評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蘸泻,死亡現(xiàn)場離奇詭異,居然都是意外死亡嘲玫,警方通過查閱死者的電腦和手機悦施,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評論 3 394
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來去团,“玉大人抡诞,你說我怎么就攤上這事穷蛹。” “怎么了沐绒?”我有些...
    開封第一講書人閱讀 164,057評論 0 354
  • 文/不壞的土叔 我叫張陵俩莽,是天一觀的道長。 經(jīng)常有香客問我乔遮,道長扮超,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,509評論 1 293
  • 正文 為了忘掉前任蹋肮,我火速辦了婚禮出刷,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘坯辩。我一直安慰自己馁龟,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,562評論 6 392
  • 文/花漫 我一把揭開白布漆魔。 她就那樣靜靜地躺著坷檩,像睡著了一般。 火紅的嫁衣襯著肌膚如雪改抡。 梳的紋絲不亂的頭發(fā)上矢炼,一...
    開封第一講書人閱讀 51,443評論 1 302
  • 那天,我揣著相機與錄音阿纤,去河邊找鬼句灌。 笑死,一個胖子當(dāng)著我的面吹牛欠拾,可吹牛的內(nèi)容都是我干的胰锌。 我是一名探鬼主播,決...
    沈念sama閱讀 40,251評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼藐窄,長吁一口氣:“原來是場噩夢啊……” “哼资昧!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起枷邪,我...
    開封第一講書人閱讀 39,129評論 0 276
  • 序言:老撾萬榮一對情侶失蹤榛搔,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后东揣,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體践惑,經(jīng)...
    沈念sama閱讀 45,561評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,779評論 3 335
  • 正文 我和宋清朗相戀三年嘶卧,在試婚紗的時候發(fā)現(xiàn)自己被綠了尔觉。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,902評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡芥吟,死狀恐怖侦铜,靈堂內(nèi)的尸體忽然破棺而出专甩,到底是詐尸還是另有隱情,我是刑警寧澤钉稍,帶...
    沈念sama閱讀 35,621評論 5 345
  • 正文 年R本政府宣布涤躲,位于F島的核電站,受9級特大地震影響贡未,放射性物質(zhì)發(fā)生泄漏种樱。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,220評論 3 328
  • 文/蒙蒙 一俊卤、第九天 我趴在偏房一處隱蔽的房頂上張望嫩挤。 院中可真熱鬧,春花似錦消恍、人聲如沸岂昭。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽约啊。三九已至,卻和暖如春佣赖,著一層夾襖步出監(jiān)牢的瞬間棍苹,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評論 1 269
  • 我被黑心中介騙來泰國打工茵汰, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人孽鸡。 一個月前我還...
    沈念sama閱讀 48,025評論 2 370
  • 正文 我出身青樓蹂午,卻偏偏與公主長得像,于是被迫代替她去往敵國和親彬碱。 傳聞我的和親對象是個殘疾皇子豆胸,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,843評論 2 354

推薦閱讀更多精彩內(nèi)容

  • react-native-side-menu 側(cè)滑欄組件1.導(dǎo)入方式切換到當(dāng)前目錄在命令行執(zhí)行下面的命令 npm ...
    以德扶人閱讀 1,589評論 2 50
  • 用到的組件 1、通過CocoaPods安裝 2巷疼、第三方類庫安裝 3晚胡、第三方服務(wù) 友盟社會化分享組件 友盟用戶反饋 ...
    SunnyLeong閱讀 14,615評論 1 180
  • 16宿命:用概率思維提高你的勝算 以前的我是風(fēng)險厭惡者,不喜歡去冒險嚼沿,但是人生放棄了冒險估盘,也就放棄了無數(shù)的可能。 ...
    yichen大刀閱讀 6,049評論 0 4
  • 公元:2019年11月28日19時42分農(nóng)歷:二零一九年 十一月 初三日 戌時干支:己亥乙亥己巳甲戌當(dāng)月節(jié)氣:立冬...
    石放閱讀 6,879評論 0 2