[iOS] 지도에 목적지까지 이동 경로 표시하기

RudinP·2024년 7월 3일
0

Study

목록 보기
240/258

어노테이션을 선택했을 때 두 지점을 잇는 경로 표시

1. mapView(didSelect annotation) 델리게이트 작성

extension ViewController: MKMapViewDelegate{
    func mapView(_ mapView: MKMapView, didSelect annotation: MKAnnotation) {
    }
}

2. MKPlacemark 객체 생성

3. MKMapItem 생성

4. MKDirections Request 객체 생성

5. Request 속성 설정

extension ViewController: MKMapViewDelegate{
    func mapView(_ mapView: MKMapView, didSelect annotation: MKAnnotation) {
        let start = mapView.userLocation.coordinate
        let dest = annotation.coordinate //선택한 어노테이션
        
        //지점의 placemark 생성
        let startPlacemark = MKPlacemark(coordinate: start)
        let destPlacemark = MKPlacemark(coordinate: dest)
        
        //mapItem 생성
        let startMapItem = MKMapItem(placemark: startPlacemark)
        let destMapItem = MKMapItem(placemark: destPlacemark)
        
        //경로는 애플 서버로 요청을 보내서 생성한다.
        let request = MKDirections.Request()
        request.source = startMapItem
        request.destination = destMapItem
        //경로를 이동하는 수단 설정
        request.transportType = .walking
        let directions = MKDirections(request: request)
    }
}

6. 경로 계산

  • 동기방식과 비동기방식이 존재한다.
  • calculateETA는 예상 도착 시간이다.

7. MKRoute 객체 반환

  • 해당 경로까지의 거리, 예상 이동시간, 이동 방식, 단계별 이동 설명이 저장되어있음.
  • 지도에 바로 추가 가능하도록 오버레이도 저장되어있음.
func mapView(_ mapView: MKMapView, didSelect annotation: MKAnnotation) {
        let start = mapView.userLocation.coordinate
        let dest = annotation.coordinate //선택한 어노테이션
        
        //지점의 placemark 생성
        let startPlacemark = MKPlacemark(coordinate: start)
        let destPlacemark = MKPlacemark(coordinate: dest)
        
        //mapItem 생성
        let startMapItem = MKMapItem(placemark: startPlacemark)
        let destMapItem = MKMapItem(placemark: destPlacemark)
        
        //경로는 애플 서버로 요청을 보내서 생성한다.
        let request = MKDirections.Request()
        request.source = startMapItem
        request.destination = destMapItem
        //경로를 이동하는 수단 설정
        request.transportType = .walking
        
        let directions = MKDirections(request: request)
        
        directions.calculate { response, error in //응답 객체, 에러
            guard let response else {
                if let error {
                    print(error)
                }
                return
            }
            
            let route = response.routes.first //routes는 배열로, 여러 경로가 나올 수 있기 때문.
        }
    }

8. addOverlay로 경로 오버레이 추가

func mapView(_ mapView: MKMapView, didSelect annotation: MKAnnotation) {
        let start = mapView.userLocation.coordinate
        let dest = annotation.coordinate //선택한 어노테이션
        
        //지점의 placemark 생성
        let startPlacemark = MKPlacemark(coordinate: start)
        let destPlacemark = MKPlacemark(coordinate: dest)
        
        //mapItem 생성
        let startMapItem = MKMapItem(placemark: startPlacemark)
        let destMapItem = MKMapItem(placemark: destPlacemark)
        
        //경로는 애플 서버로 요청을 보내서 생성한다.
        let request = MKDirections.Request()
        request.source = startMapItem
        request.destination = destMapItem
        //경로를 이동하는 수단 설정
        request.transportType = .walking
        
        let directions = MKDirections(request: request)
        directions.calculate { response, error in //응답 객체, 에러
            guard let response else {
                if let error {
                    print(error)
                }
                return
            }
            
            let route = response.routes[0] //routes는 배열로, 여러 경로가 나올 수 있기 때문.
            mapView.addOverlay(route.polyline, level: .aboveRoads)
        }
    }

9. mapView Delegate에 rendererFor 델리게이트 함수 추가

func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
        switch overlay{
        case is MKPolyline:
            let r = MKPolylineRenderer(overlay: overlay)
            r.strokeColor = .systemRed //색
            r.lineWidth = 10 //라인 두께
            return r
...

문제

도로나 인도로 인식되는 부분만 자동 처리 가능

경로로 인식되는 부분이 없다면, 기본으로 제공하는 기능은 사용 불가능하고 직접 경로를 계산해야 한다.

비동기 방식으로 수정

        let start = mapView.userLocation.coordinate
        let dest = CLLocationCoordinate2D(latitude: 37.294259, longitude: 127.2030509) //선택한 어노테이션
        
        //지점의 placemark 생성
        let startPlacemark = MKPlacemark(coordinate: start)
        let destPlacemark = MKPlacemark(coordinate: dest)
        
        //mapItem 생성
        let startMapItem = MKMapItem(placemark: startPlacemark)
        let destMapItem = MKMapItem(placemark: destPlacemark)
        
        //경로는 애플 서버로 요청을 보내서 생성한다.
        let request = MKDirections.Request()
        request.source = startMapItem
        request.destination = destMapItem
        //경로를 이동하는 수단 설정
        request.transportType = .automobile
        request.requestsAlternateRoutes = true
        
        let directions = MKDirections(request: request)
        //비동기 방식으로 경로 설정하기. 반드시 각 GeoCoder마다 다른 객체를 사용해주어야한다.
        Task{
            do{
                async let calculateResponse = directions.calculate()
                async let placemarkOfStart = CLGeocoder().reverseGeocodeLocation(CLLocation(latitude: start.latitude, longitude: start.longitude), preferredLocale: Locale(identifier: "ko_kr"))
                async let placemarksOfDest = CLGeocoder().reverseGeocodeLocation(CLLocation(latitude: dest.latitude, longitude: dest.longitude), preferredLocale: Locale(identifier: "ko_kr"))
                
                //모든 결과가 반환될때까지 대기
                let (routes, reverseGeocodedStart, reverseGeocodedDest) = try await (calculateResponse.routes, placemarkOfStart.first, placemarksOfDest.first)
                
                let route = routes[0] //routes는 배열로, 여러 경로가 나올 수 있기 때문.
                mapView.addOverlay(route.polyline, level: .aboveRoads)
                
                let region = MKCoordinateRegion(route.polyline.boundingMapRect)
                mapView.setRegion(region, animated: true)
                
                performSegue(withIdentifier: "routeSegue", sender: routes)

            } catch {
                print(error)
            }

profile
iOS 개발자가 되기 위한 스터디룸...

0개의 댓글