[SwiftUI] MapClone: Animated Menu

Junyoung Park·2022년 12월 15일
0

SwiftUI

목록 보기
131/136
post-thumbnail
post-custom-banner

Using a List as a custom animated menu | SwiftUI Map App #4

MapClone: Animated Menu

구현 목표

  • 지역 리스트 선택 메뉴 구현

구현 태스크

  • 리스트 뷰 UI 구현
  • 리스트 뷰 애니메이션 구현
  • 선택 지역 이동 로직 구현

핵심 코드

import Foundation
import MapKit

struct LocationModel: Identifiable, Equatable {
    
    let name: String
    let cityName: String
    let coordinates: CLLocationCoordinate2D
    let description: String
    let imageNames: [String]
    let link: String
    
    var id: String {
        name + cityName
    }
    
    static func == (lhs: LocationModel, rhs: LocationModel) -> Bool {
        lhs.id == rhs.id
    }
}
  • 리스트 뷰를 그릴 때 ForEach에 넣어줄 데이터 구별을 위한 Identifiable 프로토콜을 따르는 id
  • Equatable 프로토콜을 따름으로써 이후 animatedvalue로 해당 데이터를 넘겨줄 수 있음
extension LocationsView {
    private var header: some View {
        VStack {
            Button {
                viewModel.toggleLocationsList()
            } label: {
                Text(viewModel.mapLocation.name + ", " + viewModel.mapLocation.cityName)
                    .font(.title2)
                    .fontWeight(.black)
                    .foregroundColor(.primary)
                    .frame(maxWidth: .infinity, maxHeight: 55)
                    .animation(.none, value: viewModel.mapLocation)
                    .overlay(alignment: .leading) {
                        Image(systemName: "arrow.down")
                            .font(.headline)
                            .foregroundColor(.primary)
                            .padding()
                            .rotationEffect(Angle(degrees: viewModel.showLocationList ? 180 : 0))
                    }
            }

            if viewModel.showLocationList {
                LocationsListView()
            }
        }
        .background(.thinMaterial)
        .cornerRadius(10)
        .shadow(color: Color.black.opacity(0.3), radius: 20, x: 0, y: 15)
    }
}
  • 익스텐션으로 별도로 관리하는 로케이션 뷰의 헤더
  • 해당 타이틀 헤더를 클릭함으로써 뷰 모델이 가지고 있는 showLocationList 불리언 변수 값이 변경
  • 해당 값을 통해 LocationListView를 표시할지 결정
import SwiftUI

struct LocationsListView: View {
    @EnvironmentObject private var viewModel: LocationsViewModel
    var body: some View {
        List {
            ForEach(viewModel.locations) { location in
                Button {
                    viewModel.showNextLocation(location: location)
                } label: {
                    listRowView(location: location)
                }
                .padding(.vertical, 4)
                .listRowBackground(Color.clear)
            }
        }
        .listStyle(.plain)
    }
}
  • 뷰 모델이 가지고 있는 로케이션 데이터 내 이미지를 리스트 UI를 통해 표현
  • showNextLocation 함수는 리스트 내 셀 클릭 시 현재 선택된 로케이션을 변경
  • Button의 액션 및 라벨을 통해 위의 UI 및 함수를 연결
func toggleLocationsList() {
        withAnimation(.easeInOut) {
            showLocationList.toggle()
        }
    }
  • 뷰 모델의 showLocationList 불리언 변수를 토글하는 함수
  • 애니메이션 효과 적용
func showNextLocation(location: LocationModel) {
        withAnimation(.easeInOut) {
            mapLocation = location
            showLocationList = false
        }
    }
  • 뷰 모델의 mapLocation 데이터 퍼블리셔 값을 변경함으로써 현재 선택된 지역 정보를 업데이트
  • 펼쳐진 리스트 뷰를 자동으로 닫도록 showLocationList 값 또한 설정

구현 화면

profile
JUST DO IT
post-custom-banner

0개의 댓글