🦁
MapKit - 현재위치를 사용한 경로 탐색 앱 만들기
July 04, 2024
MapKit 을 사용한 예제 앱 개발
이번엔 최신 코드인 @Observable 매크로를 사용하여 viewModel 을 사용하였습니다.
@Observable는 @Published 를 따로 쓰지 않아도 상태 추적이 가능하도록 해주는 매크로입니다.
MapViewModel 생성
@Observable
class MapViewModel {
var cameraPosition: MapCameraPosition = .userLocation(fallback: .automatic)
var searchText: String = ""
}
@Observable 를 사용한 ViewModel 은 View에서 @StateObject가 아닌, @State로 불러올 수 있습니다.
import SwiftUI
import MapKit
struct ContentView: View {
@State var viewModel = MapViewModel()
var body: some View {
NavigationStack {
Map(position: $viewModel.cameraPosition)
}
}
}
만약 하위 뷰에서 해당 viewModel 을 사용하고자 한다면? 바인딩도 필요없고, environmentObject도 사용하지 않습니다. ContentView 에서 viewModel 을 완전히 소유하고 있기 때문에, 그냥 변수를 전달하면 됩니다.
// ContentView
ExtractSubView(viewModel: viewModel)
// ExtractSubView
struct ExtractSubView: View {
var viewModel: MapViewModel
}
Search Bar
그리고 이제 Navigation Title 과 검색바(searchable)를 추가하였습니다.
.navigationTitle("SearchLocation")
.searchable(text: $viewModel.searchText)
그리고 mapStyle 을 사용하기 위해서, viewModel 에 MapStyle 타입의 프로퍼티를 추가하고, mapStyle 변경은 searchScope와 onChnage 사용하여 변경할 수 있도록 하였습니다.
// MapViewModel
// ContentView
.searchScopes($style) {
Text("Standard").tag(0)
Text("Imagely").tag(1)
Text("Hybrid").tag(2)
}
.onChange(of: style) {
switch style {
case 0:
viewModel.mapStyle = .standard
case 1:
viewModel.mapStyle = .imagery
case 2:
viewModel.mapStyle = .hybrid
default:
viewModel.mapStyle = .standard
}
}
User Location Button
mapControls 를 사용하여 간단하게 추가 가능합니다.
.mapControls {
UserLocationButton()
}
그리고 이제 본격적으로 MapViewModel 이 CLLocationManagerDelegate 프로토콜을 준수하기 위한 init, 함수를 추가합니다.
// CLLocationManagerDelegate 을 준수하기 위해
// NSObject 를 추가하였고,
// NSObject 는 상속할 클래스에서 초기화를 받아야 하기 때문에 override init 호출로 바꿔줍니다.
class MapViewModel: NSObject, CLLocationManagerDelegate {
override init() {
super.init()
locationManager.delegate = self
locationManager.requestWhenInUseAuthorization()
}
// MARK: CLLocationManagerDelegate
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: any Error) {
print("error \(error.localizedDescription)")
}
}
userLocation 에 따라 MapCameraPosition 값이 변경되는 코드!
아까 만든 SearchBar 에 .onSubmit 를 추가하여 검색 기능을 호출하도록 합니다.
.onSubmit(of: .search) {
viewModel.searchLocation()
}
viewModel.searchLocation 코드입니다.
func searchLocation() {
print("search!")
let request = MKLocalSearch.Request()
request.naturalLanguageQuery = searchText
request.resultTypes = .pointOfInterest
let search = MKLocalSearch(request: request)
search.start { [weak self] response, error in
guard let response = response else { return }
self?.searchResults = response.mapItems
}
}