[iOS] Map View

RudinP·2024년 3월 26일
0

Study

목록 보기
207/226

유저 위치 표시하도록 설정하기

  • Inspector view에서 User Location 체크

유저 위치 표시하는 코드 작성 - MKCoordinateRegion

  • center 는 좌표, 두 세번째 파라미터는 지도에 표시할 구역의 크기 지정
    • 100이라고 전달하면 가로 세로 100m 범위 표시
func move(to location: CLLocation){
        let region = MKCoordinateRegion(center: location.coordinate, latitudinalMeters: 100, longitudinalMeters: 100)
        mapView.setRegion(region, animated: true)
    }

이후 extension의 didUpdateLocations 메소드에서 실행하면 된다.

func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        if let currentLocation = locations.last{
            move(to: currentLocation)
        }
    }

Map View 사용하기

1. 맵 뷰 outlet 연결

2. CLLocationManager 생성 및 델리게이트 연결

let manager = CLLocationManager()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        manager.delegate = self
    }

3. extension에 델리게이트 구현

extension MapViewController: CLLocationManagerDelegate{
    func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
        switch manager.authorizationStatus {
        case .notDetermined:
            manager.requestWhenInUseAuthorization()
            break
        case .restricted, .denied:
            let alert = UIAlertController(title: "알림", message: "위치 서비스를 활성화시켜 주세요.", preferredStyle: .alert)
            
            let settingAction = UIAlertAction(title: "설정", style: .default){ _ in
                //string에 저장된 문자열로 url을 만든 다음에 open으로 전달하면 설정 앱으로 바로 이동 가능
                if let url = URL(string: UIApplication.openSettingsURLString){
                    UIApplication.shared.open(url)
                }
            }
            alert.addAction(settingAction)
            
            let cancelAction = UIAlertAction(title: "취소", style: .cancel)
            alert.addAction(cancelAction)
            
            present(alert, animated: true)
            break
        case .authorizedAlways:
            manager.startUpdatingLocation()
            break
        case .authorizedWhenInUse:
            manager.requestAlwaysAuthorization()
        @unknown default:
            break
        }
    }
    
    func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
        //에러를 확인하려면 NSError타입으로 캐스팅해야함.
        let error = error as NSError
        guard error.code != CLError.Code.locationUnknown.rawValue else {return}
        
        print(error)
        manager.stopUpdatingLocation()
    }
    
    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        if let currentLocation = locations.last{
            move(to: currentLocation)
        }
    }

}

문제

두 개의 locationManager은 권장되지 않는다.

하나의 앱에서 locationManager을 두 개 사용해도 문제는 없으나, 지금처럼 didChangeAuthorization이 호출되지 않을 수 있음을 유의하고, 하나의 화면에서만 LocationService 를 사용할 수 있도록 유의해야 한다.
현재 짠 코드에서는 이미 Location 탭에서의 manager 인스턴스에서 권한을 받고didChangeAuthorization을 실행할 수 있었으나, Map 탭에서의 vc의 manager은 이미 권한을 획득한 상태이므로 didChangeAuthorization이 실행되지 않아, case문 안에 작성해뒀던 startUpdatingLocation 메소드가 실행되지 않는다.

해결

MapView vc에서 하단의 코드를 작성하여 권한 이 있을 때 따로 startUpdatingLocation 메소드를 실행하도록 처리해준다.
또한 화면을 벗어날 때(viewWIllDisappear) 위치 업데이트를 꺼 하나의 화면에서만 locationManager을 쓸 수 있도록 처리해준다.
이렇게 해도 startUpdatingLocation이 두 번 호출될 수 있으나, 여러번 호출한다고 해서 중복된 이벤트가 전달되는 것은 아니기 때문에 문제는 없다.

override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        
        if manager.authorizationStatus == .authorizedWhenInUse || manager.authorizationStatus == .authorizedAlways{
            manager.startUpdatingLocation()
        }
    }
    
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        
        manager.stopUpdatingLocation()
    }

  • 참고로 현재 화면은 세로가 더 긴데, 이 때는 설정한 100m보다 더 넓은 위치가 표시된다.

  • 범위를 늘릴 경우 이렇게 더 넓은 범위가 표시됨을 확인 가능하다.
profile
곰을 좋아합니다. <a href = "https://github.com/RudinP">github</a>

0개의 댓글