LoginSignup
11
8

More than 3 years have passed since last update.

iOSアプリで位置情報を取得するときに配慮する点をまとめてみた②

Last updated at Posted at 2021-01-09

前回の続き↓
iOSアプリで位置情報を取得するときに配慮する点をまとめてみた①

はじめに

前回の記事を読んでない方に、簡単に説明するとiOSアプリで位置情報を取得するときに
配慮する点をまとめた内容となっています。

前回までのあらすじ

前回、端末の位置情報サービスの有無のチェックは
アプリをインストール後の初回起動のみしかされないと書きました。

初回起動時に位置情報サービスがオフになっていたらこんなアラートを表示します↓

このチェックは初回起動のみなので、2回目以降の起動時はアラートを表示するように改修しましたね↓

今回は、バックグラウンド状態からフォアグラウンド状態にアプリを切り替えた時にも
端末の位置情報サービスの有無をチェックするようにしていきましょう。

バックグラウンド状態から戻った時にチェックする(端末)

アプリが、バックグラウンド状態からフォアグラウンド状態に切り替わった時に
端末の位置情報サービスの有無をチェックするように処理するには

AppDelegateapplicationWillEnterForeground(_ application: UIApplication)にて行います。

AppDelegateの呼ばれる順番は、こちらの記事が非常に分かりやすかったので載せておきます↓
iOSアプリのライフサイクル

AppDelegate.swift
// アプリがフォアグラウンド状態に入ろうとしている時に呼ばれるメソッド
func applicationWillEnterForeground(_ application: UIApplication)

ですが、アラートを表示したいのでViewControllerのクラス内で処理を行いたいですね。

アプリの状態が変わったことをAppDelegate以外で感知して
ある処理を行いたい場合は、NotificationCenterを使用します。

※こちらの記事を参考にして実装しました↓
[Swift3.0] NotificationCenter を使ってアプリの状態に応じた処理を行う

そして今回も、アラートの表示はこちらの方法で表示します↓
UIAlertControllerをファイルを分けて実装してみる

ViewController
// 初回表示以外にもバックグラウンド復帰、タブ切り替えなどにも呼ばれるメソッド
// まだビューが表示されていないため、計算コストの高い処理は避ける
override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    // iOSからの通知(UIApplication.willEnterForegroundNotification)を受信して登録されたメソッド(willEnterForeground)を呼び出す
    NotificationCenter.default.addObserver(self, selector: #selector(willEnterForeground), name: UIApplication.willEnterForegroundNotification, object: nil)
    }

// 画面が非表示になる直前に呼ばれるメソッド
override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        // 常に監視されているクラス(アプリで共通、UserDefaultと同じ感じ)なので呼び出したら毎回、削除(バグが起こりやすいため)

        // 例えば、NavigationControllerを使ってViewController1 > 2 > 3に画面遷移し、全てのViewControllerでNotificationCenterを呼び、willEnterForegroundNotificationのメソッドを書いていたとする
        // 画面遷移したとしても、ViewController 1,2,3は消えない(メモリー上に残っている)ので
        // ViewController3の画面内でバックグラウンド復帰した場合、他のViewController1,2も反応してしまうため毎回、削除しなくてはならない
        NotificationCenter.default.removeObserver(self, name: UIApplication.willEnterForegroundNotification, object: nil)
}

// バックグラウンド状態から戻ってきた時に呼ばれるメソッド(自分で作る)
@objc private func willEnterForeground() {
    // バックグラウンド状態から戻ってきた時に端末の位置情報サービスがオフの場合
    if !CLLocationManager.locationServicesEnabled() {
         // アラート表示
         Alert.okAlert(vc: self, title: "位置情報サービスを\nオンにして下さい", message: "「設定」アプリ ⇒「プライバシー」⇒「位置情報サービス」からオンにできます")
   }
}

ビルドしてみると、バックグラウンド状態から戻った時にもアラートが表示されるようになりました↓
ezgif.com-gif-maker.gif
これで、端末の位置情報サービスの有無を起動時バックグラウンド状態から戻った時
チェックするようになりましたね。

後、チェックしなければいけないのはアプリの位置情報サービスの有無です。

アプリの位置情報サービスの有無をチェックする

位置情報を取得する際には、ユーザーに許可を貰わなければ取得出来ません。
なので、アプリはこのようなアラートを表示してユーザーに許可をリクエストします↓

ですが、もし、ユーザーが許可しないを選択したらどうでしょうか?
位置情報が必要なアプリの場合、許可を貰わなければ困りますよね。

なのでユーザーが、許可しないを選択した場合にアラートなどを表示したりして
アプリの位置情報取得の許可を促さなければいけません。

前回のソースコードを例に説明します。
※ちょっと長いので一部抜粋

ViewController
import UIKit
import CoreLocation

private var locationManager: CLLocationManager = {
    var locationManager = CLLocationManager()
    locationManager.desiredAccuracy = kCLLocationAccuracyKilometer
    locationManager.distanceFilter = 5
    return locationManager
}()

override func viewDidLoad() {
   super.viewDidLoad()
   locationManager.requestWhenInUseAuthorization()
   locationManager.delegate = self
  }

extension ViewController: CLLocationManagerDelegate {
    func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
     if CLlocationManager.locationServicesEnabled() {

        let status = manager.authorizationStatus

        switch status {
        case .authorizedAlways, .authorizedWhenInUse:
            manager.startUpdatingLocation()

        case .notDetermined:
            manager.requestWhenInUseAuthorization()

        // 許可しない場合
        case .denied:
            // アラートを表示して、アプリの位置情報サービスをオンにするように促す
            Alert.okAlert(vc: self, title: "アプリの位置情報サービスを\nオンにして下さい", message: "")

        case .restricted:
            break

        default:
            break
        }
    }else {
       Alert.okAlert(vc: self, title: "位置情報サービスを\nオンにして下さい", message: "「設定」アプリ ⇒「プライバシー」⇒「位置情報サービス」からオンにできます")
    }
  }
}

許可しないを選択した場合、アラートがちゃんと表示されました。

これでも、ちゃんとユーザーに対してアプリの位置情報取得の許可を促していますが
ここで、もう少し配慮してアラートのOKを選択したら設定アプリに画面遷移するようにしましょう。

設定アプリに画面遷移する

設定アプリに画面遷移するメリットとしてアプリの位置情報取得の許可がスムーズになり
ユーザーの手間が省かれることです。

画面遷移する方法は、こちらの記事が分かりやすかったので載せておきます↓
Swift5を使ってURLスキームで設定画面に遷移する方法

この方法を用いて、先ほどのソースコードに追加していきます。

ViewController
import UIKit
import CoreLocation

private var locationManager: CLLocationManager = {
    var locationManager = CLLocationManager()
    locationManager.desiredAccuracy = kCLLocationAccuracyKilometer
    locationManager.distanceFilter = 5
    return locationManager
}()

override func viewDidLoad() {
   super.viewDidLoad()
   locationManager.requestWhenInUseAuthorization()
   locationManager.delegate = self
  }

extension ViewController: CLLocationManagerDelegate {
    func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
        if CLLocationManager.locationServicesEnabled() {    

        let status = manager.authorizationStatus

        switch status {
        case .authorizedAlways, .authorizedWhenInUse:
            manager.startUpdatingLocation()

        case .notDetermined:
            manager.requestWhenInUseAuthorization()

        // 許可しない場合
        case .denied:
            // アラートを表示して、アプリの位置情報サービスをオンにするように促す
            // ユーザーに対して分かりやすようにmessageで、OKを選択すると設定アプリに画面遷移することを伝える
            Alert.okAlert(vc: self, title: "アプリの位置情報サービスを\nオンにして下さい", message: "OKをタップすると設定アプリに移動します") { (_) in
               // OKを選択した後、設定アプリに画面遷移する
               UIApplication.shared.open(URL(string: UIApplication.openSettingsURLString)!, options: [:], completionHandler: nil)
            }

        case .restricted:
            break

        default:
            break
        }
    }else {
       Alert.okAlert(vc: self, title: "位置情報サービスを\nオンにして下さい", message: "「設定」アプリ ⇒「プライバシー」⇒「位置情報サービス」からオンにできます")
    }
  }
}

こちらをビルドしてみると、ちゃんと設定アプリに画面遷移しました。
ですが、ここでも落とし穴があります。
ezgif.com-gif-maker2.gif

この方法で、設定アプリに移動したのはいいけど
アプリの位置情報取得の許可をしないまま戻ったとしましょう。

このままだと、アプリは何もチェックしません。

この場面でも、アプリがバックグラウンド状態から戻った時に
アプリの位置情報取得の有無をチェックしなければいけません。

またまた記事が長くなりそうなので、今回はここら辺で終わります。
ここ間違っているよー!というのがありましたら、気軽にコメントして下さい。

最後まで読んで下さって、ありがとうございます!
ここまでのソースコードを下に載せておきます↓

ソースコード

ViewController
import UIKit
import CoreLocation

final class ViewController: UIViewController {

    private var locationManager: CLLocationManager = {
       var locationManager = CLLocationManager()
       locationManager.desiredAccuracy = kCLLocationAccuracyKilometer
       locationManager.distanceFilter = 5
       return locationManager
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
        locationManager.requestWhenInUseAuthorization()
        locationManager.delegate = self
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        NotificationCenter.default.addObserver(self, selector: #selector(willEnterForeground), name: UIApplication.willEnterForegroundNotification, object: nil)
    }

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        NotificationCenter.default.removeObserver(self, name: UIApplication.willEnterForegroundNotification, object: nil)
    }

    @objc private func willEnterForeground() {
        if !CLLocationManager.locationServicesEnabled() {
            Alert.okAlert(vc: self, title: "位置情報サービスを\nオンにして下さい", message: "「設定」アプリ ⇒「プライバシー」⇒「位置情報サービス」からオンにできます")
        }
    }
}

extension ViewController: CLLocationManagerDelegate {
    func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
        if CLLocationManager.locationServicesEnabled() { 

        let status = manager.authorizationStatus

        switch status {
        case .authorizedAlways, .authorizedWhenInUse:
            manager.startUpdatingLocation()

        case .notDetermined:
            manager.requestWhenInUseAuthorization()

        case .denied:
            Alert.okAlert(vc: self, title: "アプリの位置情報サービスを\nオンにして下さい", message: "OKをタップすると設定アプリに移動します") { (_) in
                UIApplication.shared.open(URL(string: UIApplication.openSettingsURLString)!, options: [:], completionHandler: nil)
            }

        case .restricted:
            break           

        default:
            break
        }
      }else {
        Alert.okAlert(vc: self, title: "位置情報サービスを\nオンにして下さい", message: "「設定」アプリ ⇒「プライバシー」⇒「位置情報サービス」からオンにできます")
      }
    }

    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        guard let gps = manager.location?.coordinate else {
            return
        }
        manager.stopUpdatingLocation()
        let lat = gps.latitude
        let lng = gps.longitude
        print("経度:\(String(describing: lat)), 緯度:\(String(describing: lng))")
    }
}
Alert
import UIKit

final class Alert {
    static func okAlert(vc: UIViewController,title: String, message: String, handler: ((UIAlertAction) -> Void)? = nil) {
        let okAlertVC = UIAlertController(title: title, message: message, preferredStyle: .alert)
        okAlertVC.addAction(UIAlertAction(title: "OK", style: .default, handler: handler))
        vc.present(okAlertVC, animated: true, completion: nil)
    }
}

続き

iOSアプリで位置情報を取得するときに配慮する点をまとめてみた③

11
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
8