チュートリアル - Xamarin.iOS でのバックグラウンドの位置情報
この例では、現在の位置 (緯度、経度、その他のパラメーター) に関する情報を画面に出力する iOS Location アプリケーションを作成します。 このアプリケーションでは、アプリケーションがアクティブであるかバックグラウンドにある間に位置情報の更新を適切に実行する方法を示します。
このチュートリアルでは、アプリをバックグラウンドで必要なアプリケーションとして登録する、アプリがバックグラウンドになったときに UI の更新を中断する、 WillEnterBackground
メソッドと WillEnterForeground
AppDelegate
メソッドを操作するなど、いくつかの主要な背景概念について説明します。
アプリケーションの設定
まず、新しい iOS> アプリの>単一ビュー アプリケーション (C#) を作成します。 それに Location という名前を付け、iPad と iPhone の両方が選択されていることを確認します。
位置情報アプリケーションは、iOS ではバックグラウンド処理が必要なアプリケーションとして分類されます。 プロジェクトの Info.plist ファイルを編集して、アプリケーションを Location アプリケーションとして登録します。
ソリューション エクスプローラーで Info.plist ファイルをダブルクリックして開き、一覧の一番下までスクロールします。 [バックグラウンド モードを有効にする] と [位置情報の更新] の両方のチェックボックスをオンにします。
Visual Studio for Mac では、次のようになります。
Visual Studio では、次のキーと値のペアを追加して、Info.plist を手動で更新する必要があります。
<key>UIBackgroundModes</key> <array> <string>location</string> </array>
これでアプリケーションが登録され、デバイスから位置情報データを取得できるようになりました。 iOS では、
CLLocationManager
クラスを使用して位置情報にアクセスし、位置情報の更新を提供するイベントを発生させることができます。このコードでは、
LocationManager
という新しいクラスを作成します。このクラスは、さまざまな画面とコードが位置情報の更新をサブスクライブするための単一の場所を提供します。LocationManager
クラスで、LocMgr
というCLLocationManager
のインスタンスを作成します。public class LocationManager { protected CLLocationManager locMgr; public LocationManager () { this.locMgr = new CLLocationManager(); this.locMgr.PausesLocationUpdatesAutomatically = false; // iOS 8 has additional permissions requirements if (UIDevice.CurrentDevice.CheckSystemVersion (8, 0)) { locMgr.RequestAlwaysAuthorization (); // works in background //locMgr.RequestWhenInUseAuthorization (); // only in foreground } if (UIDevice.CurrentDevice.CheckSystemVersion (9, 0)) { locMgr.AllowsBackgroundLocationUpdates = true; } } public CLLocationManager LocMgr { get { return this.locMgr; } } }
上記のコードは、CLLocationManager クラスに対するいくつかのプロパティとアクセス許可を設定します。
PausesLocationUpdatesAutomatically
– これは、システムが位置情報の更新を一時停止できるかどうかに応じて設定可能なブール値です。 一部のデバイスでは既定でtrue
に設定されており、、デバイスが約 15 分後にバックグラウンドでの位置情報の更新の取得を停止する可能性があります。RequestAlwaysAuthorization
- アプリのユーザーに、バックグラウンドでの位置情報へのアクセスを許可するオプションを提供するには、このメソッドを渡す必要があります。 また、アプリがフォアグラウンドにあるときだけ位置情報へのアクセスを許可するオプションをユーザーに提供する場合には、RequestWhenInUseAuthorization
を渡すこともできます。AllowsBackgroundLocationUpdates
– これは iOS 9 で導入されたブール型プロパティで、アプリが中断された時に位置情報の更新を受信できるように設定できます。
重要
iOS 8 (およびそれ以降) では、認証要求の一部としてユーザーを表示するために、Info.plist ファイル内のエントリも必要です。
アプリに必要なアクセス許可の種類 (
NSLocationAlwaysUsageDescription
、NSLocationWhenInUseUsageDescription
、および/またはNSLocationAlwaysAndWhenInUseUsageDescription
) に Info.plist キーを追加します。これには、位置情報データへのアクセスを要求するアラート内でユーザーに表示する文字列を指定します。iOS 9 では、
AllowsBackgroundLocationUpdates
を使用する場合、Info.plist に値location
を持つキーUIBackgroundModes
が含まれている必要があります。 このチュートリアルのステップ 2 を完了している場合、これは既に Info.plist ファイルに含まれています。LocationManager
クラス内に、次のコードを使用してStartLocationUpdates
というメソッドを作成します。 このコードは、CLLocationManager
から位置情報の更新の受信を開始する方法を示しています。if (CLLocationManager.LocationServicesEnabled) { //set the desired accuracy, in meters LocMgr.DesiredAccuracy = 1; LocMgr.LocationsUpdated += (object sender, CLLocationsUpdatedEventArgs e) => { // fire our custom Location Updated event LocationUpdated (this, new LocationUpdatedEventArgs (e.Locations [e.Locations.Length - 1])); }; LocMgr.StartUpdatingLocation(); }
このメソッドでは、いくつかの重要なことが発生します。 まず、アプリケーションがデバイス上の位置情報データにアクセスできるかどうかのチェックを実行します。
CLLocationManager
でLocationServicesEnabled
を呼び出すことによってこれを確認します。 ユーザーがアプリケーションによる位置情報へのアクセスを拒否した場合、このメソッドは false を返します。次に、更新する頻度を位置情報マネージャーに伝えます。
CLLocationManager
には、更新の頻度など、位置情報データをフィルター処理および構成するためのオプションが多数用意されています。 この例では、位置情報が 1 メートル変わるたびにDesiredAccuracy
が更新されるように設定します。 位置情報の更新頻度などの基本設定の構成の詳細については、Apple ドキュメントの「CLLocationManager クラス リファレンス」を参照してください。最後に、
CLLocationManager
インスタンスでStartUpdatingLocation
を呼び出します。 これは、位置情報マネージャに対して、現在の位置情報の初期位置を取得し、更新の送信を開始するように指示します。
これまでに、位置情報マネージャーの作成、受信するデータの種類の構成、初期位置の決定が行われました。 次に、コードで位置情報データをユーザー インターフェイスにレンダリングする必要があります。 これを行うには、引数として CLLocation
を受け取るカスタム イベントを使用します。
// event for the location changing
public event EventHandler<LocationUpdatedEventArgs>LocationUpdated = delegate { };
次のステップでは、CLLocationManager
からの位置情報の更新をサブスクライブし、新しい位置情報データが利用可能になったときにカスタム LocationUpdated
イベントを発生させ、その位置情報を引数として渡します。 これを行うには、新しいクラス LocationUpdateEventArgs.cs を作成します。 このコードはメイン アプリケーション内でアクセス可能であり、イベントが発生したときにデバイスの位置情報を返します。
public class LocationUpdatedEventArgs : EventArgs
{
CLLocation location;
public LocationUpdatedEventArgs(CLLocation location)
{
this.location = location;
}
public CLLocation Location
{
get { return location; }
}
}
ユーザー インターフェイス
Xcode Interface Builder を使用して、位置情報を表示する画面を作成します。 Main.storyboard ファイルをダブルクリックして開始します。
ストーリーボード上で、位置情報のプレースホルダーとして機能するラベルをいくつか画面にドラッグします。 この例では、緯度、経度、高度、コース、速度のラベルがあります。
詳細については、「Xcode を使用したユーザーインターフェイスの設計」を参照してください。
Solution Pad で
ViewController.cs
ファイルをダブルクリックして編集し、LocationManager の新しいインスタンスを作成してStartLocationUpdates
を呼び出します。 コードを次のように変更します。#region Computed Properties public static bool UserInterfaceIdiomIsPhone { get { return UIDevice.CurrentDevice.UserInterfaceIdiom == UIUserInterfaceIdiom.Phone; } } public static LocationManager Manager { get; set;} #endregion #region Constructors public ViewController (IntPtr handle) : base (handle) { // As soon as the app is done launching, begin generating location updates in the location manager Manager = new LocationManager(); Manager.StartLocationUpdates(); } #endregion
これにより、アプリケーションの起動時に位置情報の更新が開始されますが、データは表示されません。
位置情報の更新を受信したので、位置情報を使用して画面を更新します。 次のメソッドは、
LocationUpdated
イベントから位置情報を取得し、UI に表示します。#region Public Methods public void HandleLocationChanged (object sender, LocationUpdatedEventArgs e) { // Handle foreground updates CLLocation location = e.Location; LblAltitude.Text = location.Altitude + " meters"; LblLongitude.Text = location.Coordinate.Longitude.ToString (); LblLatitude.Text = location.Coordinate.Latitude.ToString (); LblCourse.Text = location.Course.ToString (); LblSpeed.Text = location.Speed.ToString (); Console.WriteLine ("foreground updated"); } #endregion
AppDelegate の LocationUpdated
イベントをサブスクライブし、新しいメソッドを呼び出して UI を更新する必要があります。 StartLocationUpdates
呼び出しの直後、ViewDidLoad,
に次のコードを追加します。
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
// It is better to handle this with notifications, so that the UI updates
// resume when the application re-enters the foreground!
Manager.LocationUpdated += HandleLocationChanged;
}
アプリケーションを実行すると、次のようになります。
アクティブおよびバックグラウンド状態の処理
アプリケーションはフォアグラウンドでアクティブな間、位置情報の更新を出力します。 アプリがバックグラウンドに入るときに何が起こるかを示すために、アプリケーションの状態の変化を追跡する
AppDelegate
メソッドをオーバーライドして、アプリケーションがフォアグラウンドとバックグラウンドの間を遷移するときにコンソールに書き込むようにします。public override void DidEnterBackground (UIApplication application) { Console.WriteLine ("App entering background state."); } public override void WillEnterForeground (UIApplication application) { Console.WriteLine ("App will enter foreground"); }
LocationManager
に次のコードを追加して、更新された位置情報データをアプリケーションの出力に継続的に表示し、バックグラウンドで引き続き位置情報が利用可能であることを確認します。public class LocationManager { public LocationManager () { ... LocationUpdated += PrintLocation; } ... //This will keep going in the background and the foreground public void PrintLocation (object sender, LocationUpdatedEventArgs e) { CLLocation location = e.Location; Console.WriteLine ("Altitude: " + location.Altitude + " meters"); Console.WriteLine ("Longitude: " + location.Coordinate.Longitude); Console.WriteLine ("Latitude: " + location.Coordinate.Latitude); Console.WriteLine ("Course: " + location.Course); Console.WriteLine ("Speed: " + location.Speed); } }
コードには問題が 1 つ残っています。アプリがバックグラウンドにあるときに UI を更新しようとすると、iOS によってアプリが終了されます。 アプリがバックグラウンドに移行すると、コードは位置情報の更新のサブスクライブを解除し、UI の更新を停止する必要があります。
iOS は、アプリが別のアプリケーションの状態に移行しようとしているときに通知を出します。 この場合、
ObserveDidEnterBackground
通知をサブスクライブできます。次のコード スニペットは、通知を使用してビューに UI の更新を停止するタイミングを通知する方法を示しています。 これは
ViewDidLoad
に追加されます。UIApplication.Notifications.ObserveDidEnterBackground ((sender, args) => { Manager.LocationUpdated -= HandleLocationChanged; });
アプリが実行されると、出力は次のようになります。
アプリケーションは、フォアグラウンドで動作しているときは画面に位置情報の更新を出力し、バックグラウンドで動作している間もアプリケーション出力ウィンドウにデータを出力し続けます。
未解決の問題が 1 つだけ残っています。アプリが最初に読み込まれたときに画面が UI の更新を開始しますが、アプリがいつフォアグラウンドに戻ったのかを知る方法がありません。 バックグラウンド アプリケーションがフォアグラウンドに戻っても、UI の更新が再開されません。
これを修正するには、アプリケーションがアクティブ状態になったときに発生する別の通知内に UI の更新を開始する呼び出しを入れ子にします。
UIApplication.Notifications.ObserveDidBecomeActive ((sender, args) => {
Manager.LocationUpdated += HandleLocationChanged;
});
これで、アプリケーションの初回起動時に UI の更新が開始され、アプリがフォアグラウンドに戻るたびに更新が再開されます。
このチュートリアルでは、画面とアプリケーション出力ウィンドウの両方に位置情報データを出力する、適切に動作するバックグラウンド対応の iOS アプリケーションを作成しました。