-
Swift 위치정보를 받아서 행정구역 단위로 나타내기(역 지오코딩, reverse geocoding)iOS/Swift 2022. 1. 13. 02:33
안녕하세요!! 이번엔 역 지오코딩을 구현하는 방법을 정리했습니다.
이번 글도 노션에서 옮겨와 말이 짧습니다!! 양해해주세용😽
mkMapView를 공부하면서 위치정보를 나타내는 것에 대해 작성해보려고 한다.
먼저, 사용자의 위치정보를 받아오려면 위치정보에 대한 권한을 요청해야한다. Info.Plist에 위치와 관련된 속성들이 있다.
- Privacy - Location When In Use Usage DescriptioniOS
- 앱이 foreground에서 실행 중일 때에만 위치 정보에 액세스하는 경우 사용한다.
- iOS 11 이상에서 사용 가능하다.
- Privacy - Location Always and When In Use Usage DescriptioniOS
- 앱이 background에서 실행되는 동안 위치 정보에 액세스하는 경우 사용한다.
- iOS 11 이상에서 사용 가능하다.
- Privacy - Location Default Accuracy Reduced
- 위치 정확도에 대한 앱의 동작을 설정하려면 Info.Plist에 이 키를 포함할 수 있다.
- true: 위치 정확도가 떨어지는지 묻는 메시지가 표시되도록 키 값을 설정한다.
- reducedAccuracy만을 이용한다.
- 대략적인 위치만을 제공한다.
- false: 전체 위치 정확도를 묻는 메세지가 표시되도록 설정한다.
- fullAccuracy와 reducedAccuracy 이용가능
- 사용자가 정확한 위치를 제공할지 말지 선택할 수 있다.
- 키를 명시해두지 않을 경우, false가 기본값으로 설정된다.
- iOS 14 이상에서 사용 가능하다.
- Privacy - Location Temporary Usage Description Dictionary
- 앱이 사용자 위치에 대한 임시 액세스를 요청하는 이유를 설명한다.
- 예를 들어, 앱이 앱의 한 부분에서 근처에 있는 카페를 제안하고, 다른 부분에서 근처에 있는 친구를 찾는 경우 두 항목을 포함한다. → 친구를 찾아야 할 때 친구를 찾기위해 위치정보가 필요하다고 요청, 카페를 찾을 때 위치정보가 필요하다고 요청
- 앱이 사용자 위치에 대한 임시 액세스를 요청하는 이유를 설명한다.
- Privacy - Location Usage Description
- macOS앱이 사용자의 위치 정보에 액세스하는 API를 사용하는 경우에만 필요
이중에서 앱이 사용중일 경우에만 지도를 사용할 것이기 때문에
Privacy - Location When In Use Usage Description 속성을 Info.plist에 추가해주었다.
그리고 미리 MapKit을 이용하여 만들어둔 ViewController로 가서 CoreLocation을 import 해주었다.
var locataionManager: CLLocationManager = CLLocationManager() override func viewDidLoad() { super.viewDidLoad() locataionManager.delegate = self }
그리고 위치 관련 이벤트를 처리하기 위해 CLLocationManager 객체를 만들어주고 delegate 메소드를 사용하기 위해 viewDidLoad에서 delegate 시켜주었다.
그리고 사용자의 위치 권한을 확인하는 코드를 추가해주었다.
// 사용자의 위치 서비스 권한 확인 func checkUsersLocationServicesAutorization() { let auth: CLAuthorizationStatus if #available(iOS 14.0, *) { auth = locataionManager.authorizationStatus } else { auth = CLLocationManager.authorizationStatus() } // iOS 위치 서비스 권한 확인 if CLLocationManager.locationServicesEnabled() { checkCurrentLocationAutorization(status: auth) } }
func checkCurrentLocationAutorization(status: CLAuthorizationStatus) { switch status { case .notDetermined: print("설정되지 않았음") locataionManager.requestWhenInUseAuthorization() locataionManager.desiredAccuracy = kCLLocationAccuracyBest locataionManager.startUpdatingLocation() // 위치정보를 받아올 수 없는 상황이거나 사용자가 거부했을 경우, 서울시청을 기본 Location으로 지정 case .restricted, .denied: setLocation(latitude: 37.56674435790457, longitude: 126.9784350966443) print("제한됨. 설정으로 이동") case .authorizedAlways, .authorizedWhenInUse: print("이용가능") self.locataionManager.startUpdatingLocation() @unknown default: print("unknown") } if #available(iOS 14.0, *) { let accuracyState = locataionManager.accuracyAuthorization switch accuracyState { case .fullAccuracy: print("Full") case .reducedAccuracy: print("reduced") @unknown default: print("Default") } } }
위 함수에서 startUpdatingLocation을 호출하여, 사용자의 현재 위치에 대해 업데이트를 시작할 수 있다.
실제로 값을 받게 되면, CLLocationManagerDelegate 프로토콜의 didUpdateLocations 메소드가 실행된다.
extension MapViewController: CLLocationManagerDelegate { func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { if let coordinate = locations.last?.coordinate { // location 정보를 받아 행정구역을 나타낼 수 있는 getCurrentAddress함수 (커스텀) getCurrentAddress(location: CLLocation(latitude: coordinate.latitude, longitude: coordinate.longitude)) // 위도 경도를 담아둘 프로퍼티를 만들었다. self.locationCoordinator = CLLocationCoordinate2D(latitude: coordinate.latitude, longitude: coordinate.longitude) // 작업이 끝날때 stopUpdatingLocation을 호출해 불필요한 위치 데이터 업데이트를 멈추게 한다. locataionManager.stopUpdatingLocation() } } // 현재 위치 정보를 가져오는 것을 실패했을 때 func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) { print("didFailWithError: \\(error)") } // iOS 14 이상에서 사용 가능 func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) { print(#function) checkUsersLocationServicesAutorization() } // iOS 14 미만에서 사용 가능 func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) { print(#function) checkUsersLocationServicesAutorization() } }
didUpdateLocations의 메소드의 매개변수들은
manager는 locationManager, locations는 CLLocation의 배열이 들어온다.
이 배열에는 마지막 위치에 대한 데이터를 포함하고 있다.
데이터를 전송하기 전에 여러 위치에 대한 정보가 저장될 수 있기 때문에 배열로 들어오고,
가장 최근의 위치가 배열의 가장 끝에 위치한다.
func getCurrentAddress(location: CLLocation) { let coder = CLGeocoder() let locale = Locale(identifier: "ko-KR") coder.reverseGeocodeLocation(location, preferredLocale: locale) { (placemark, error) -> Void in guard error == nil, let place = placemark?.first else { print("주소 설정 불가능") return } if let subLocality = place.subLocality { self.locationAddress += " \\(subLocality)" print(subLocality) } } }
getCurrentAddress 함수는 location 정보를 받아 행정구역으로 decoding 해주는 함수다.
한국어로 출력하기 위해 한국 식별자를 가지고 있는 locale 객체를 만들어주었다.
CLGeocoder의 reverseGeocodeLocation를 호출해서 placemark들을 받아올 수 있는데, placemark에는 국가, 지역, 주, 도시 및 거리주소 등의 정보가 포함되어있다.
- administrativeArea
- 시/도
- locality
- 구
- subLocality
- 동
var locationAddress: String = UserDefaults.standard.string(forKey: "location") ?? "" { didSet { self.navigationItem.title = locationAddress UserDefaults.standard.set(locationAddress, forKey: "location") } }
위에서 가져온 위치 정보를 다른 곳에서도 사용할 수 있으니 UserDefaults에 저장했다. 프로퍼티 옵저버를 달아놓고 위치가 바뀌어서 localAddress가 바뀔 때 마다 재설정 될 수 있도록 코드를 작성했다.
시뮬레이터를 실행하면 navigationTitle에 원하는 행정구역으로 작성이 된 것을 볼 수 있다! 😃
'iOS > Swift' 카테고리의 다른 글
Swift SocketIO를 활용한 실시간 채팅 앱 구현 (0) 2022.01.15 Swift Localization 처리하기(로컬라이징, 현지화) (0) 2022.01.14 Delegate 패턴을 이용하여 TableViewCell 내부의 버튼 이벤트 처리하기 (0) 2022.01.13 UISearchBar에서 사용자가 입력을 멈췄을 때 자동으로 검색하기(실시간 검색) (0) 2022.01.11 특정 모서리에만 CornerRadius 적용하기 (0) 2022.01.11 - Privacy - Location When In Use Usage DescriptioniOS