상세 컨텐츠

본문 제목

[iOS/Swift] MKMapKit, CLCoreLocation 으로 지도앱 만들기- 소스코드 제공

Swift

by Mr.Garlic 2022. 2. 3. 20:28

본문

MKMapKit과 CoreLocation을 활용해 간단한 앱 만들기

 

 

GitHub - AnnaBaeTofuMom/MapKitPlay: 맵킷을 처음 사용하시는 분들의 이해를 돕는 간단한 지도앱

맵킷을 처음 사용하시는 분들의 이해를 돕는 간단한 지도앱. Contribute to AnnaBaeTofuMom/MapKitPlay development by creating an account on GitHub.

github.com

먼저 위의 레포지토리에서 프로젝트를 다운로드 받아주세요! 이미 권한 처리 까지 다 되어있는 소스코드 입니다! 

유용하셨다면 star를 부탁드립니다!

지도를 활용한 간단한 xcode project 와 에셋이 들어있습니다. (Xcode가 당연히 필요하겠죠?) 

아래와 같은 예시 앱을 만들어 볼거예요. 

 

만약 깃헙 활용이 아직 미숙하시다면 아래의 프로젝트 파일을 받으셔도 됩니다..!

MapKitPlay.zip
0.04MB

 

 

참고 ⚠️

오늘은 뷰를 짜는 방법은 따로 설명하지 않을 거예요. 첨부된 파일을 열어 MapView 라는 파일을 열어보시면 스냅킷을 사용해 코드베이스로 UI를 만들어 두었으니 뷰 부터 궁금하신 분은 참고해주세요. 

영등포에 위치한 새싹 캠퍼스와 유저의 현재위치가 드러나는 앱을 다운받을 수 있습니다. 

먼저 알아야 할 것

 

지도 위에 무엇인가를 표시해야 한다면 어노테이션과 오버레이, 이 두 가지를 보시면 됩니다. 애플 개발자 문서의 MapKit부분을 잠깐이라도 흝어보고 오시길 추천드립니다! 

애플 개발자 문서 바로가기 

 

이전 포스팅에서 다루었던 러닝앱을 만들 당시에는 오버레이를 주로 사용했지만 오늘은 오버레이에 대해서는 다루지 않을 예정입니다. 오버레이는 말하자면 지도를 가지고 그림판처럼 쓰실 수 있다고 생각하면 돼요. 선(Polyline)도 그리고 색깔도 바꾸고 그런게 됩니다. 오늘은 우리에게 필요한 어노테이션을 집중해서 볼거예요!

어노테이션이 뭔가요?

지도위에 뭐 표시할때, 추파춥스처럼 생긴 핀을 꼽아서 위치를 표기하고 인터랙션도 하고 그렇죠?

그런걸 어노테이션이라고 보시면 됩니다.

어노테이션의 기본 모양

우리가 만들려고 하는 앱은 어노테이션의 모양이 햇님 모양입니다. 기본 어노테이션과는 다른데요. 어떻게하면 커스텀 어노테이션을 만들 수 있는지도 함께 보도록 하겠습니다. 

MapKit의 역할

이번 프로젝트에서 맵 킷의 역할은 세 가지 입니다. (delegate포함)

  1. 지도를 불러와 주는 역할
  2. 지도의 영역(span)을 지정해 주는 역할
  3. 지도 위 어노테이션을 관리해 주는 역할

지도 불러오기

첨부된 프로젝트의 MapView 파일에 가보시면 

let map = MKMapView()

이렇게 맵뷰 인스턴스를 만들어 준 것을 보실 수 있을거예요. 이게 지도를 보여주는 뷰 라고 생각하시면 됩니다. 

이렇게 만들어준 뷰를 화면에 꽉 차게 배치했어요. 스토리 보드를 사용하시는 경우에도 MapView를 검색하셔서 화면에 꽉 차게 넣으셔도 똑같이 동작할거예요. 

class MapView: UIView {


    let sesacLocationButton = UIButton()
    let myLocationButton = UIButton()
    let map = MKMapView()
    
    .
    .
    .
    
    }

 

지도의 영역 설정하기

지도앱을 켰는데 보여주는 영역이 대륙급 스케일(?) 이라면 유저가 얼마나 당황할까요ㅎㅎ

그래서 용도에 따라 적절한 축척(지도의 축척!!)을 활용해주는 것이 중요합니다.

조금 더 직관적으로 얘기하자면 지도가 얼마나 확대될 것 인지를 정해주는 것이지요.

 

불필요한 코드를 약간 지우고 코드를 한번 보겠습니다. 혹시 놓치는 분들 있을까봐 Delegate 프로토콜 사용하는 부분은 지우지 않았어요! 

class ViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate { //LocationManager를 사용하기 위한 Delegate
    
    let mapView = MapView()
    
    override func loadView() {
        view = mapView
    }
    
    override func viewDidLoad() {
        
        super.viewDidLoad()
        
        mapView.map.delegate = self
        
        mapView.map.setRegion(MKCoordinateRegion(center: "최초로 포커스 시킬 위치좌표", span: MKCoordinateSpan(latitudeDelta: 0.01, longitudeDelta: 0.01)), animated: true)
        
        
    }

맨 아래 map.setRegion()을 통해 처음 시작 화면에 바로 내가 원하는 위치로 보내도록 설정해보겠습니다. 

이때 Center은 일단 무시하고, span 먼저 보겠습니다. 지도의 영역을 표기할 때, span이라는 네모 상자의 크기를 지정해주면 되는데요. 

MKCoordinateSpan의 latitude, longitude의 숫자를 바꿔가면서 빌드해보세요. 영역이 커지고 작아지는 것을 확인하실 수 있을거예요. 

 

지도 위의 어노테이션 관리하기

ViewController 클래스에 addCustomPin이라는 함수를 정의해 주었습니다. 

pin 이라는 기본 어노테이션(추파춥스 모양 그것)을 만들어주고 좌표(coordinate)를 찍어주었어요.

그리고 이 어노테이션을 지도위에 표시할 수 있도록 넣어주었습니다. 

단일 좌표를 만드는 함수이긴 하지만 조금 변형하시면 그때 그때 새로운 어노테이션을 만들어 나가는 것도 가능할 것입니다. 

 private func addCustomPin() {
        let pin = MKPointAnnotation()
        pin.coordinate = sesacCoordinate
        pin.title = "새싹 영등포캠퍼스"
        pin.subtitle = "전체 3층짜리 건물"
        mapView.map.addAnnotation(pin)
    }

 

이렇게 따라해보시면 아마 어노테이션 모양이 귀여운 햇님이 아니라 추파춥스 모양이라 아쉬우실겁니다. 그래서 커스텀 어노테이션을 등록해보도록 할게요. 이 부분은 tableView의 reusableCell의 기전과 굉장히 유사합니다! 여러번 재사용할 수 있게 핀을 등록해주는 것이라서요. 

 //재사용 할 수 있는 어노테이션 만들기! 마치 테이블뷰의 재사용 Cell을 넣어주는 것과 같아요!
    func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
        guard !annotation.isKind(of: MKUserLocation.self) else {
            // 유저 위치를 나타낼때는 기본 파란 그 점 아시죠? 그거 쓰고싶으니까~ 요렇게 해주시고 만약에 쓰고싶은 어노테이션이 있다면 그녀석을 리턴해 주시면 되긋죠? 하하!
            return nil
        }
        //우리가 만들고 싶은 커스텀 어노테이션을 만들어 줍시다. 그냥 뿅 생길 수 없겠죠? 보여주고 싶은 모양을 뷰로 짜준다고 생각하시면 됩니다.
        //즉시 인스턴스로 만들어 줘 보겠습니다요. 어떻게 생겼을지는 아직 안정했지만 일단 커스텀이라는 식별자?를 가진 뷰로 만들어 줬습니다.
        //마커 어노테이션뷰 라는 어노테이션뷰를 상속받는 뷰가 따로있습니다. 풍선모양이라고 하는데 한번 만들어 보시는것도 좋겠네요! 테두리가 있고 안에 내용물을 바꾸는 식으로 설정이 되는듯 해요.
        var annotationView = self.mapView.map.dequeueReusableAnnotationView(withIdentifier: "Custom")
        
        if annotationView == nil {
            //없으면 하나 만들어 주시고
            annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: "Custom")
            annotationView?.canShowCallout = true
            
            
            //callOutView를 통해서 추가적인 액션을 더해줄수도 있겠죠! 와 무지 간편합니다!
            let miniButton = UIButton(frame: CGRect(x: 0, y: 0, width: 20, height: 20))
            miniButton.setImage(UIImage(systemName: "person"), for: .normal)
            miniButton.tintColor = .blue
            annotationView?.rightCalloutAccessoryView = miniButton
            
        } else {
            //있으면 등록된 걸 쓰시면 됩니다.
            annotationView?.annotation = annotation
        }
        
        annotationView?.image = UIImage(named: "Circle")
        
        //상황에 따라 다른 annotationView를 리턴하게 하면 여러가지 모양을 쓸 수도 있겠죠?
        
        return annotationView
    }

모든 내용은 예제 파일에도 포함되어 있으니 천천히 이해해 보고 변형해 보시길 바랍니다!

 

 

CLCoreLocation과 LocationManager 사용하기

위치를 가지고 할 수 있는 것은 정말 많지만, 대표적으로 많이 쓰이는 것은 역시 위치 정보를 핸들링 할 수 있는 LocationManager가 아닐까요? 로케이션 매니저 인스턴스를 만들어 줍니다. 

let locationManager = CLLocationManager()

그렇다면 CoreLocation을 사용할 준비가 끝난 것입니다. 

 

class ViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate { //LocationManager를 사용하기 위한 Delegate
    
    let mapView = MapView()

    let locationManager = CLLocationManager() //위치를 조종하게(?) 도와주는 로케이션 매니저를 하나 고용합시다.
    
    	override func loadView() {
        	view = mapView
   		}
    
    	override func viewDidLoad() {
        
        	super.viewDidLoad()
            
            	locationManager.delegate = self
        
        	locationManager.requestWhenInUseAuthorization() //권한요청하기
        
        
    	}
    
    
    }

 

CLCoreLocation의 역할

이번에 만드는 앱에서 CoreLocation의 역할은 다음과 같아요. 

  1. 유저의 위치 정보를 받아오기
  2. 유저에게 위치 정보 권한 받기

아래의 함수가 내 위치 찾기 버튼을 누를때 마다 실행되도록 해 두었는데요. 

옵셔널 바인딩 부분(guard let 이하)은 유저의 현재 위치를 받아올 수 있는 locationManager.location 이 nil이라면 유저의 위치정보권한 상태를 확인하는 코드가 실행되도록 해 둔 것입니다. 

@objc func findMyLocation() {
        
        guard let currentLocation = locationManager.location else {
            checkUserLocationServicesAuthorization()
            return
        }
        
        mapView.map.showsUserLocation = true
        
        mapView.map.setUserTrackingMode(.follow, animated: true)
        
    }

유저의 위치 정보 받아오기

유저의 현재 위치를 알 수 있는 권한만 설정이 되어 있다면 지도가 유저의 위치를 표시할 수 있습니다. 

이 부분은 MapKit의 영역이긴 하지만 위치를 받아오는 것이 선행되어야 해요. 

유저의 위치를 받아올 수 있다면 showsUserLocation과 setUserTrackingMode를 활용해 현재 유저의 위치를 드러내고, 움직임에 따라 지도가 같이 움직이도록 설정해 두세요. 

 

유저의 위치 정보 권한 받기

이 부분은 별개의 포스팅으로 다룰 예정입니다. 물론 예제 파일에도 코드가 있으니 참고해보세요. 위치 정보를 받아오는 코드도 다들 다른데요. 지금 제가 올려둔 코드는 권한이 없을 때 설정 화면까지 이동시켜주는 코드입니다. 

 

swift/iOS 위치 정보 얻기, 위치 권한 설정하기.

xcode에서 위치 정보 얻어오기. swift에서는 위치 정보를 받아오기 위해 얻어와도 되는지 물어보고, 물어본 상태에 따라 로직을 구현해 나가는 과정이 필요하다. 한번이라도 해봤으면 어떤 느낌인

42kchoi.tistory.com

 

이것으로 간단한 위치 앱을 만들때 필요한 객체를 가볍게 훑어 보았습니다! 혹시 궁금한 점이 있으시면 말씀해주세요!

코드상으로는 구현을 해 두었는데 설명을 다 하지 않은 부분이 있습니다. (Coordinate 받기, delegate 활용 등)

둘러보시면서 간단히 이해가 가능하니 여러 위치로 바꿔서 사용해보세요.

 


😇 MapKit을 활용해 만들어 보시면 좋을 것들

거리 계산하기

  • 위치(coordinate)를 알면 할 수 있는게 매우 많습니다!
  • 위치와 위치 간의 직선거리를 구해주는 메소드 distance를 활용해보셔도 좋겠죠! 저는 러닝앱을 출시할 때, 이 메소드로 매번 이동한 거리를 더해주는 방식으로 이동거리를 구하기도 했어요. 

경로 찾기

  • MKDirections를 사용해서 현재 위치와 가고싶은 위치를 설정하면 경로도 찾아준답니다.
  • MKDirectionsTransportType을 사용해서 자동차용, 도보용 경로를 따로 받을 수도 있어요.

어노테이션을 그룹화 하기

지도를 줌아웃했는데 어노테이션이 그득하면 약간 환공포증이겠죠?

Cluster Annotation을 사용하면 멀리 줌 아웃 했을 때 어노테이션을 예쁘게 묶어주기도 해요!

  • 배열을 사용해서 클러스터 어노테이션에 멤버 어노테이션을 넣어주면 됩니다.
  • 줌인과 줌아웃에 따라 알아서 보여줍니다. 겁나 편리하고 간단한 세상입니다.

그 밖에도...

  • 미리 특정 위치를 지정해 두면 유저가 그 위치에 들어갔을 때 노티를 받을 수 있어요. 포켓몬고 할때 포켓스탑가까이에 가면 활성화되고 그러잖아요? 요런 기술이 들어가지 않았을까 생각합니다.

 

오늘은 예제 앱을 활용해 MKMapKit과 CoreLocation을 간단히 활용해보는 시간을 가졌습니다. 

다음 포스팅에는 다른 라이브러리를 소개해드리고 싶네요!

혹시 궁금한 점이 있으시다면 자유롭게 댓글을 부탁드립니다.

 

감사합니다 :) 

관련글 더보기