[SwiftUI] TwitterClone: SideMenu

Junyoung ParkΒ·2022λ…„ 11μ›” 16일
0

SwiftUI

λͺ©λ‘ 보기
101/136
post-thumbnail
post-custom-banner

πŸ”΄ Let's Build Twitter with SwiftUI (iOS 15, Xcode 13, Firebase, SwiftUI 3.0)

TwitterClone: SideMenu

κ΅¬ν˜„ λͺ©ν‘œ

  • μ‚¬μ΄λ“œ 메뉴 UI κ΅¬ν˜„

κ΅¬ν˜„ νƒœμŠ€ν¬

  • μ΅œμƒλ‹¨ 컨텐츠 λ·°: μ‚¬μ΄λ“œ 메뉴 + νƒ­λ°” ꡬ성
  • μ‚¬μ΄λ“œ 메뉴 클릭 이벀트(λ„€λΉ„κ²Œμ΄μ…˜) κ΅¬ν˜„

핡심 μ½”λ“œ

SideMenuView()
                .frame(width: 300)
                .offset(x: showMenu ? 0 : -300, y: 0)
                .background(showMenu ? Color.white : Color.clear)
  • νƒ­ λ·° 상단에 ZStackκ³Ό ν•¨κ»˜ μ„ μ–Έλœ μ‚¬μ΄λ“œ 메뉴 λ·°
  • showMenuκ°€ @State ν”„λ‘œνΌν‹°μ΄κΈ° λ•Œλ¬Έμ— κ΄€μ°° κ°€λŠ₯, ν˜„μž¬ 상황에 λ§žμ·ƒ x κ°’ λ³€κ²½
.toolbar {
            ToolbarItem(placement: .navigationBarLeading) {
                Button {
                    withAnimation(.easeInOut) {
                        showMenu.toggle()
                    }
                } label: {
                    Circle()
                        .frame(width: 32, height: 32)
                }

            }
        }
  • μ• λ‹ˆλ©”μ΄μ…˜μ„ 톡해 ν† κΈ€ κ°€λŠ₯
ForEach(SideMenuViewModel.allCases, id:\.rawValue) { viewModel in
                    if viewModel == .profile {
                        SideMenuOptionRowView(viewModel: viewModel)
                            .onTapGesture {
                                showProfileView.toggle()
                            }
                    } else if viewModel == .logout {
                        SideMenuOptionRowView(viewModel: viewModel)
                            .onTapGesture {
                                print("Handle logout here...")
                            }

                    } else {
                        SideMenuOptionRowView(viewModel: viewModel)
                    }
                }
  • μ‚¬μ΄λ“œ 메뉴 뷰의 각 μ˜΅μ…˜ λ³„λ‘œ μ„œλ‘œ λ‹€λ₯Έ 행동을 핸듀링
.background(NavigationLink(destination: ProfileView(), isActive: $showProfileView, label: {
            EmptyView()
        }))
  • 미리 λ„€λΉ„κ²Œμ΄μ…˜ 링크λ₯Ό μœ„ν•΄ λ·°λ₯Ό μ„ μ–Έν•˜μ§€ μ•Šκ³  ν•΄λ‹Ή showProfileView ν”„λ‘œνΌν‹° 값이 참일 λ•Œμ—λ§Œ μ΄λ‹ˆμ…œλΌμ΄μ¦ˆ ν›„ ν‘Έμ‹œ

μ†ŒμŠ€ μ½”λ“œ

import SwiftUI

@main
struct TwitterCloneApp: App {
    var body: some Scene {
        WindowGroup {
            NavigationView {
                ContentView()
            }
        }
    }
}
  • μ‚¬μ΄λ“œλ°” μΈν„°λ ‰μ…˜μ„ μœ„ν•œ λ„€λΉ„κ²Œμ΄μ…˜ λ·°
import SwiftUI

struct ContentView: View {
    @State private var showMenu: Bool = false
    var body: some View {
        ZStack(alignment: .topLeading) {
            MainTabView()
                .navigationBarHidden(showMenu)
            if showMenu {
                ZStack {
                    Color(.black)
                        .opacity(showMenu ? 0.25 : 0)
                        .onTapGesture {
                            withAnimation(.easeInOut) {
                                showMenu = false
                            }
                        }
                }
                .ignoresSafeArea()
            }
            SideMenuView()
                .frame(width: 300)
                .offset(x: showMenu ? 0 : -300, y: 0)
                .background(showMenu ? Color.white : Color.clear)
        }
        .navigationTitle("Home")
        .navigationBarTitleDisplayMode(.inline)
        .toolbar {
            ToolbarItem(placement: .navigationBarLeading) {
                Button {
                    withAnimation(.easeInOut) {
                        showMenu.toggle()
                    }
                } label: {
                    Circle()
                        .frame(width: 32, height: 32)
                }

            }
        }
        .onAppear {
            showMenu = false
        }
    }
}
  • 컨텐츠 λ·°λŠ” ZStack을 톡해 μ‚¬μ΄λ“œ 메뉴 및 νƒ­λ·° μ—°κ²°
  • @State둜 μ„ μ–Έν•œ ν”„λ‘œνΌν‹° κ°’ 변경을 κ΄€μ°° κ°€λŠ₯ν•˜κΈ° λ•Œλ¬Έμ— μ• λ‹ˆλ©”μ΄μ…˜ κ°€λŠ₯
import SwiftUI

struct SideMenuView: View {
    @State private var showProfileView: Bool = false
    var body: some View {
        ZStack {
            VStack(alignment: .leading, spacing: 32) {
                VStack(alignment: .leading) {
                    Circle()
                        .frame(width: 48, height: 48)
                    VStack(alignment: .leading, spacing: 4) {
                        Text("Peter Parker")
                            .font(.headline)
                        Text("@SpiderMan")
                            .font(.caption)
                            .foregroundColor(.gray)
                    }
                    UserStatsView()
                        .padding(.vertical)
                }
                .padding(.leading)
                
                ForEach(SideMenuViewModel.allCases, id:\.rawValue) { viewModel in
                    if viewModel == .profile {
                        SideMenuOptionRowView(viewModel: viewModel)
                            .onTapGesture {
                                showProfileView.toggle()
                            }
                    } else if viewModel == .logout {
                        SideMenuOptionRowView(viewModel: viewModel)
                            .onTapGesture {
                                print("Handle logout here...")
                            }

                    } else {
                        SideMenuOptionRowView(viewModel: viewModel)
                    }
                }
                Spacer()
            }
        }
        .background(NavigationLink(destination: ProfileView(), isActive: $showProfileView, label: {
            EmptyView()
        }))
    }
}
  • ν”„λ‘œν•„ μ˜΅μ…˜ 선택 μ‹œμ—λ§Œ μ΄λ‹ˆμ…œν•˜κΈ° μœ„ν•΄ isActiveλ₯Ό 톡해 λ„€λΉ„κ²Œμ΄μ…˜ λ·° 바인딩
import SwiftUI

struct SideMenuOptionRowView: View {
    let viewModel: SideMenuViewModel
    var body: some View {
        HStack(spacing: 16) {
            Image(systemName: viewModel.imageName)
                .font(.headline)
                .foregroundColor(.gray)
            Text(viewModel.title)
                .font(.subheadline)
            Spacer()
        }
        .frame(height: 40)
        .padding(.horizontal)
        .background(Color(.systemBackground).opacity(0.001))
    }
}
  • μ˜΅μ…˜ λ³„λ‘œ μž¬μ‚¬μš©ν•˜κΈ° μœ„ν•œ μ‚¬μ΄λ“œ 메뉴 μ˜΅μ…˜ 둜우 λ·° κ΅¬ν˜„

κ΅¬ν˜„ ν™”λ©΄

profile
JUST DO IT
post-custom-banner

0개의 λŒ“κΈ€