6
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

[CLLocationManager]ジオフェンスを利用するための実装手順

Posted at

はじめに

ジオフェンスを利用したiOSアプリを開発する機会があったので、備忘録としてまとめたいと思います。

そもそもジオフェンスとは?

仮想的な境界線(半径100mで設定したならば、半径100mの円)を引くことで、
その境界線の侵入離脱をトリガーに自分が行いたい処理を行うことができます。

例えば、

  • 通知
  • 他機器との接続

などが行えたりします。

通知であれば、ある領域内に入ったときに、
その領域付近のおすすめのお店を出したり、お店のチラシ情報を出したり使い道はいくらでもありそうです。
しかし、登録できる領域の個数に制限があるので、その部分には配慮が必要です。

動作環境

iOS13.3
xcode11.5

実装手順

1. フレームワークの導入

位置情報やジオフェンスを利用するために、CoreLocationをimportします。

import CoreLocation

2. CLLocationManagerのインスタンス生成

メンバ変数として、CLLocationManagerとモニタリング領域に関するCLCircularRegionのインスタンスを作成します。

let locationManager: CLLocationManager = CLLocationManager()
var moniteringRegion: CLCircularRegion = CLCircularRegion()

3. 位置情報取得のユーザー許可設定

アプリで位置情報を利用するには、ユーザー認証を行わなければなりません。

// 位置情報取得のユーザー認証
self.locationManager.requestAlwaysAuthorization()

これに加えて、info.plistへ
Privacy - Location When In Use Usage Description
を設定する必要があり、表示する文言をValueに書くことができます。

4. デリゲートの設定

デリゲートを設定します。
この設定をしないと、後に記載するデリゲートメソッドが反応しなくなります。

// デリゲートの設定
self.locationManager.delegate = self

5. ジオフェンス領域の設定

ジオフェンス領域を設定したい中心地点の緯度・経度を設定し、半径を入力します。

// 中心位置の設定(緯度・経度)
let moniteringCordinate = CLLocationCoordinate2DMake(100.0, 100.0)
// モニタリング領域を作成
self.moniteringRegion = CLCircularRegion.init(center: moniteringCordinate, radius: 100.0, identifier: "name")

6. モニタリング開始・停止・状態取得

5で作成したモニタリング領域を利用することで、モニタリングの開始・停止・状態取得を行えます。

// モニタリング開始
self.locationManager.startMonitoring(for: self.moniteringRegion)

// モニタリング停止
self.locationManager.stopMonitoring(for: self.moniteringRegion)

// 現在の状態(領域内or領域外)を取得
self.locationManager.requestState(for: self.moniteringRegion)

7. 位置情報・ジオフェンスに関するデリゲートメソッド

以下は、デリゲートメソッドです。

extension ViewController: CLLocationManagerDelegate {
    
    // 位置情報の更新時に呼ばれる
    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        let location = locations.first!
        print("緯度:\(location.coordinate.latitude)、経度:\(location.coordinate.longitude)")
    }
    
    // 位置情報取得認可
    func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
        switch status {
        case .notDetermined:
            print("ユーザー認証未選択")
            break
        case .restricted:
            print("位置情報サービス未許可")
            break
        case .denied:
            print("位置情報取得を拒否、もしくは本体設定で拒否")
            break
        case .authorizedAlways:
            print("アプリは常時、位置情報取得を許可")
            break
        case .authorizedWhenInUse:
            print("アプリ起動時のみ、位置情報取得を許可")
            break
        }
    }
    
    // モニタリング開始成功時に呼ばれる
    func locationManager(_ manager: CLLocationManager, didStartMonitoringFor region: CLRegion) {
        print("モニタリング開始")
    }

    // モニタリングに失敗時に呼ばれる
    func locationManager(_ manager: CLLocationManager, monitoringDidFailFor region: CLRegion?, withError error: Error) {
        print("モニタリング失敗")
    }

    // ジオフェンス領域侵入時に呼ばれる
    func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) {
        print("ジオフェンス侵入")
    }

    // ジオフェンス領域離脱時に呼ばれる
    func locationManager(_ manager: CLLocationManager, didExitRegion region: CLRegion) {
        print("ジオフェンス離脱")
    }

    // ジオフェンスの情報が取得できないときに呼ばれる
    func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
        print("モニタリングエラー")
    }

    // requestStateが呼ばれた時に呼ばれる
    public func locationManager(_ manager: CLLocationManager, didDetermineState state: CLRegionState, for region: CLRegion) {
        if state == .inside {
            print("領域内です。")
        } else {
            print("領域外です。")
        }
    }
}

まとめ

以上の手順で、実装を行うことでジオフェンスを手軽に利用することができます。

実際にジオフェンスを利用すると、位置情報の精度にもよりますが、
なかなかジオフェンス侵入・離脱が予期した位置で反応してくれない場合が多々あります。

特に、離脱に関しては、
最短でも半径+100m程度の距離で離脱のデリゲートメソッドが呼ばれているように感じました。

また、バックグラウンドでジオフェンスを利用する場合には、
他設定が必要になってくるので、また今後記事にしたいと思います。

コード全体

import UIKit
import CoreLocation

class ViewController: UIViewController {
    
    let locationManager: CLLocationManager = CLLocationManager()
    var moniteringRegion: CLCircularRegion = CLCircularRegion()

    override func viewDidLoad() {
        super.viewDidLoad()
        
        // 位置情報取得のユーザー認証
        self.locationManager.requestAlwaysAuthorization()
        
        // デリゲートの設定
        self.locationManager.delegate = self
        
        // 中心位置の設定(緯度・経度)
        let moniteringCordinate = CLLocationCoordinate2DMake(100.0, 100.0)
        // モニタリング領域を作成
        self.moniteringRegion = CLCircularRegion.init(center: moniteringCordinate, radius: 100.0, identifier: "name")
    }
    
    @IBAction func moniteringStartButton(_ sender: Any) {
        // モニタリング開始
        self.locationManager.startMonitoring(for: self.moniteringRegion)
    }
    
    @IBAction func moniteringStopButton(_ sender: Any) {
        // モニタリング停止
        self.locationManager.stopMonitoring(for: self.moniteringRegion)
    }
    
    @IBAction func requestStateButton(_ sender: Any) {
        // 現在の状態(領域内or領域外)を取得
        self.locationManager.requestState(for: self.moniteringRegion)
    }
    
}

extension ViewController: CLLocationManagerDelegate {
    // 位置情報の更新時に呼ばれる
    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        let location = locations.first!
        print("緯度:\(location.coordinate.latitude)、経度:\(location.coordinate.longitude)")
    }

    // 位置情報取得認可
    func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
        switch status {
        case .notDetermined:
            print("ユーザー認証未選択")
            break
        case .restricted:
            print("位置情報サービス未許可")
            break
        case .denied:
            print("位置情報取得を拒否、もしくは本体設定で拒否")
            break
        case .authorizedAlways:
            print("アプリは常時、位置情報取得を許可")
            break
        case .authorizedWhenInUse:
            print("アプリ起動時のみ、位置情報取得を許可")
            break
        }
    }

    // モニタリング開始成功時に呼ばれる
    func locationManager(_ manager: CLLocationManager, didStartMonitoringFor region: CLRegion) {
        print("モニタリング開始")
    }

    // モニタリングに失敗時に呼ばれる
    func locationManager(_ manager: CLLocationManager, monitoringDidFailFor region: CLRegion?, withError error: Error) {
        print("モニタリング失敗")
    }

    // ジオフェンス領域侵入時に呼ばれる
    func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) {
        print("ジオフェンス侵入")
    }

    // ジオフェンス領域離脱時に呼ばれる
    func locationManager(_ manager: CLLocationManager, didExitRegion region: CLRegion) {
        print("ジオフェンス離脱")
    }

    // ジオフェンスの情報が取得できないときに呼ばれる
    func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
        print("モニタリングエラー")
    }
    
    // requestStateが呼ばれた時に呼ばれる
    public func locationManager(_ manager: CLLocationManager, didDetermineState state: CLRegionState, for region: CLRegion) {
        if state == .inside {
            print("領域内です。")
        } else {
            print("領域外です。")
        }
    }
}
6
4
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
6
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?