Swift/iOS MapKit 으로 지도에 이동 기록 나타내기(with LocalDB Realm)
안녕하세요 kchoi입니다.
오늘은 MapKit을 사용하는 법부터 MapKit을 가지고 Map 위에 우리가 움직인 기록을 그려보도록 하겠습니다.
그리고 그 결과를 LocalDB에 저장하고 그 데이터를 불러와보고, 또 삭제도 해봅시다.
우리가 지도를 가지고 할 수 있는 것들은 매우 다양합니다.
그중에서 지도 자체를 가지고 놀려면 역시 내 위치를 띄우는 것이 기본이 되겠죠.
그런데 위치를 띄우기만 하는 것 가지고는 만족하지 못한 저는 제가 움직인 이동 동선을 지도에 그려보고 싶었습니다.
예를 들어 다음 그림처럼요.
(시뮬레이터의 위치가 미국이라서 영어로 된 길들이 보이네요 ^^;)
그럼 본격적으로 한번 글을 시작해보겠습니다.
실험환경은 : Xcode 12.5 / Swift5 입니다.
MapKit으로 지도 띄우기
MapKit으로 지도부터 띄워봅시다.
먼저 import를 할건데, 위치를 받아오기 위해 CoreLocation
을,
MapKit을 위해 MapKit
을,
마지막으로 있다가 Local DB 를 사용하기 위해 RealmSwift
를 import 해줍니다.
그리고 storyBoard에 Map Kit View를 하나 올려주고 원하는 크기로 constraints를 부여한 뒤,
@IBOutlet weak var mapKit: MKMapView!
처럼 선언해줍니다.
그런 다음 MapKit이 어플을 종료하고 다시 실행했을 때 발생할 수 있는 오류를 방지해주기 위해 우리가 선언한 변수를 MKMapType.standard
로 선언해줍니다. 이렇게요: self.MapView.mapType = MKMapType.standard
그리고 지도에 내 위치를 표시하기 위해서 self.MapView.showsUserLocation = true
로 설정해주고,
현재 내 위치 기준으로 지도를 움직이기 위해서 self.MapView.setUserTrackingMode(.follow, animated: true)
로 설정해줍니다.
Location 정보 얻어오기 권한 설정하기
여기 글에서 정리했듯이 우리는 gps data를 얻어오기 위해 몇 가지 설정을 해줘야 합니다.
지난 번에 정리한 적이 있기 때문에 자세한 설명은 생략하고 어떤 것들을 코드로 적어줬는지만 정리해놓겠습니다.
구체적인 것들이 궁금하신 분들은 위 링크를 타고 들어가주세요 !
1 - locationManager 생성.
lazy var locationManager: CLLocationManager = {
let manager = CLLocationManager()
manager.desiredAccuracy = kCLLocationAccuracyBest
manager.startUpdatingLocation() // startUpdate를 해야 didUpdateLocation 메서드가 호출됨.
manager.delegate = self
return manager
}()
2 - locationManager를 통해 gps 사용 허가 받기
override func viewDidLoad() {
super.viewDidLoad()
getLocationUsagePermission()
...
}
func getLocationUsagePermission() {
self.locationManager.requestWhenInUseAuthorization()
}
3 - view가 화면에서 사라질 때 locationManager가 위치 업데이트를 중단하도록 하기
override func viewWillDisappear(_ animated: Bool) {
self.locationManager.stopUpdatingLocation()
}
4 - GPS 권한 설정 여부에 따른 로직 처리하기
(아래 TrackMyTraceViewController는 제가 만든 뷰 컨트롤러의 이름입니다.)
extension TrackMyTraceViewController: CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
switch status {
case .authorizedAlways, .authorizedWhenInUse:
print("GPS 권한 설정됨")
case .restricted, .notDetermined:
print("GPS 권한 설정되지 않음")
DispatchQueue.main.async {
self.getLocationUsagePermission()
}
case .denied:
print("GPS 권한 요청 거부됨")
DispatchQueue.main.async {
self.getLocationUsagePermission()
}
default:
print("GPS: Default")
}
}
...
}
5 - Info.plist에 다음을 추가하기.
여기까지 했다면 Map이 지도에 잘 출력될 것입니다.
시뮬레이터의(혹은 테스트 기기의) 위치 또한 정상적으로 잡혀 있을 것입니다.
이동 기록 그려내기
이번에는 맵 위에 이동 기록을 그려내보겠습니다.
이동 기록을 그리는 아이디어는 이렇습니다:
위치를 업데이트 한다 -> 위치가 업데이트 되면 이전 위치와 현재 위치를 선으로 잇는다.
먼저 이동 기록을 그려내기 위해서는 이전 위치를 저장해야 합니다.
그래서 저는 CLLocationManagerDelegate
를 구현한 extension에서
func locationManager(_ manger: CLLocationManager, didUpdateLocations locations: [CLLocation]) {}
을 구현했습니다.
그리고 그것을 구현할 때 필요한 변수가 var previousCoordinate: CLLocationCoordinate2D?
이니까 미리 선언해둡시다.
그 코드는 다음과 같습니다.
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let location = locations.last
else {return}
let latitude = location.coordinate.latitude
let longtitude = location.coordinate.longitude
if let previousCoordinate = self.previousCoordinate {
var points: [CLLocationCoordinate2D] = []
let point1 = CLLocationCoordinate2DMake(previousCoordinate.latitude, previousCoordinate.longitude)
let point2: CLLocationCoordinate2D
= CLLocationCoordinate2DMake(latitude, longtitude)
points.append(point1)
points.append(point2)
let lineDraw = MKPolyline(coordinates: points, count:points.count)
self.MapView.addOverlay(lineDraw)
}
self.previousCoordinate = location.coordinate
}
코드를 설명하자면 point1과 point2에 각각 이전 위치, 현재 위치를 담고 그것들을 MKPolyline으로 잇고,
내가 만든 mapView 객체 위에 그려내는 것입니다.
MKOverlayRenderer로 그려보기
위에서 설명한 코드에서 self.MapView.addOverlay(lineDraw)
를 하면
func mapView(...rendererFor overlay: MKOverlay...) -> MKOverlayRenderer
함수가 호출됩니다.
이 함수는 MKMapViewDelegate
를 구현해야 사용할 수 있습니다.
extension을 맨 아래에 추가해서 MKMapViewDelegate
를 내 ViewController가 구현하도록 만들어주고,
그 안에 해당 함수를 적어줍시다.
이렇게요:
extension TrackMyTraceViewController: MKMapViewDelegate {
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
guard let polyLine = overlay as? MKPolyline
else {
print("can't draw polyline")
return MKOverlayRenderer()
}
let renderer = MKPolylineRenderer(polyline: polyLine)
renderer.strokeColor = .orange
renderer.lineWidth = 5.0
renderer.alpha = 1.0
return renderer
}
}
위 함수는 polyline 객체가 들어왔을 때 그것을 그리는 메서드입니다.
해당 polyline의 색깔, 두께 등을 지정할 수 있습니다.
그리고 해당 delegate를 사용하기 위해서는 viewDidLoad() 시에 꼭 !
self.MapView.delegate = self
처럼 delegate를 현재 VC로 설정해주어야 합니다.
여기까지 하셨다면 이제 지도를 켜봅시다.
잘 나오시나요?
저는 다음 사진처럼 나오고 있습니다 :)
혹시나 안 되시는 분들을 위해 전체 코드를 올려놓겠습니다.
//
// TrackMyTraceViewController.swift
// HoTechCourse
//
// Created by 최강훈 on 2021/05/06.
//
import UIKit
import CoreLocation
import RealmSwift
import MapKit
class TrackMyTraceViewController: UIViewController {
@IBOutlet weak var MapView: MKMapView!
lazy var locationManager: CLLocationManager = {
let manager = CLLocationManager()
manager.desiredAccuracy = kCLLocationAccuracyBest
manager.startUpdatingLocation()
manager.delegate = self
return manager
}()
var previousCoordinate: CLLocationCoordinate2D?
override func viewDidLoad() {
super.viewDidLoad()
getLocationUsagePermission()
// xcode 종료 후 어플을 다시 실행했을 때 뜨는 오류 방지.
self.MapView.mapType = MKMapType.standard
// 지도에 내 위치 표시
MapView.showsUserLocation = true
// 현재 내 위치 기준으로 지도를 움직임.
self.MapView.setUserTrackingMode(.follow, animated: true)
self.MapView.isZoomEnabled = true
self.MapView.delegate = self
self.trackData.date = Date()
}
func getLocationUsagePermission() {
self.locationManager.requestWhenInUseAuthorization()
}
override func viewWillDisappear(_ animated: Bool) {
self.locationManager.stopUpdatingLocation()
}
}
extension TrackMyTraceViewController: CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
switch status {
case .authorizedAlways, .authorizedWhenInUse:
print("GPS 권한 설정됨")
case .restricted, .notDetermined:
print("GPS 권한 설정되지 않음")
DispatchQueue.main.async {
self.getLocationUsagePermission()
}
case .denied:
print("GPS 권한 요청 거부됨")
DispatchQueue.main.async {
self.getLocationUsagePermission()
}
default:
print("GPS: Default")
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let location = locations.last
else {return}
let latitude = location.coordinate.latitude
let longtitude = location.coordinate.longitude
self.labelLocationInfo1?.text
= "위도: \(latitude) / 경도: \(longtitude)"
if let previousCoordinate = self.previousCoordinate {
var points: [CLLocationCoordinate2D] = []
let point1 = CLLocationCoordinate2DMake(previousCoordinate.latitude, previousCoordinate.longitude)
let point2: CLLocationCoordinate2D
= CLLocationCoordinate2DMake(latitude, longtitude)
points.append(point1)
points.append(point2)
let lineDraw = MKPolyline(coordinates: points, count:points.count)
self.MapView.addOverlay(lineDraw)
}
self.previousCoordinate = location.coordinate
}
}
extension TrackMyTraceViewController: MKMapViewDelegate {
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
guard let polyLine = overlay as? MKPolyline
else {
print("can't draw polyline")
return MKOverlayRenderer()
}
let renderer = MKPolylineRenderer(polyline: polyLine)
renderer.strokeColor = .orange
renderer.lineWidth = 5.0
renderer.alpha = 1.0
return renderer
}
}
여기까지 잘 따라하셨다면 원하시는 정보를 모두 얻으셨을 거라 생각합니다.
혹시나 localDB(Realm) 을 어떻게 쓰는지 궁금해서 들어오신 분들을 위해 github 주소를 남겨놓을테니 clone해서 봐주세요!
해당 github repo 내 - HoTechCourse - TrackMyTrace 폴더입니다.
그리고 여기에 Realm 사용법을 따로 정리해두었으니 여기도 참고하시면 좋을 것 같습니다 :)
github:
'iOS::스위프트(swift) > just swift' 카테고리의 다른 글
iOS/Swift 앱의 라이프 사이클 (0) | 2021.05.11 |
---|---|
iOS/Swift 푸시 알림 원리 (0) | 2021.05.11 |
Swift/iOS Naver API 받아 써보기(feat. Nmapsmap 오류 해결) (0) | 2021.05.03 |
swift/iOS 위치 정보 얻기, 위치 권한 설정하기. (0) | 2021.04.30 |
ios/swift 이미지뷰에 앨범 이미지 넣기(이미지 뷰를 탭하여) (0) | 2021.03.11 |
최근댓글