次の方法で共有


チュートリアル - Xamarin.iOS でのバックグラウンドの位置情報

この例では、現在の位置 (緯度、経度、その他のパラメーター) に関する情報を画面に出力する iOS Location アプリケーションを作成します。 このアプリケーションでは、アプリケーションがアクティブであるかバックグラウンドにある間に位置情報の更新を適切に実行する方法を示します。

このチュートリアルでは、アプリをバックグラウンドで必要なアプリケーションとして登録する、アプリがバックグラウンドになったときに UI の更新を中断する、 WillEnterBackground メソッドと WillEnterForeground AppDelegate メソッドを操作するなど、いくつかの主要な背景概念について説明します。

アプリケーションの設定

  1. まず、新しい iOS> アプリの>単一ビュー アプリケーション (C#) を作成します。 それに Location という名前を付け、iPad と iPhone の両方が選択されていることを確認します。

  2. 位置情報アプリケーションは、iOS ではバックグラウンド処理が必要なアプリケーションとして分類されます。 プロジェクトの Info.plist ファイルを編集して、アプリケーションを Location アプリケーションとして登録します。

    ソリューション エクスプローラーで Info.plist ファイルをダブルクリックして開き、一覧の一番下までスクロールします。 [バックグラウンド モードを有効にする][位置情報の更新] の両方のチェックボックスをオンにします。

    Visual Studio for Mac では、次のようになります。

    [バックグラウンド モードを有効にする] と [位置情報の更新] の両方のチェックボックスをオンにします

    Visual Studio では、次のキーと値のペアを追加して、Info.plist を手動で更新する必要があります。

    <key>UIBackgroundModes</key>
    <array>
      <string>location</string>
    </array>
    
  3. これでアプリケーションが登録され、デバイスから位置情報データを取得できるようになりました。 iOS では、CLLocationManager クラスを使用して位置情報にアクセスし、位置情報の更新を提供するイベントを発生させることができます。

  4. このコードでは、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 ファイル内のエントリも必要です。

  5. アプリに必要なアクセス許可の種類 (NSLocationAlwaysUsageDescriptionNSLocationWhenInUseUsageDescription、および/または NSLocationAlwaysAndWhenInUseUsageDescription) に Info.plist キーを追加します。これには、位置情報データへのアクセスを要求するアラート内でユーザーに表示する文字列を指定します。

  6. iOS 9 では、AllowsBackgroundLocationUpdates を使用する場合、Info.plist に値 location を持つキー UIBackgroundModes が含まれている必要があります。 このチュートリアルのステップ 2 を完了している場合、これは既に Info.plist ファイルに含まれています。

  7. 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();
    }
    

    このメソッドでは、いくつかの重要なことが発生します。 まず、アプリケーションがデバイス上の位置情報データにアクセスできるかどうかのチェックを実行します。 CLLocationManagerLocationServicesEnabled を呼び出すことによってこれを確認します。 ユーザーがアプリケーションによる位置情報へのアクセスを拒否した場合、このメソッドは false を返します。

  8. 次に、更新する頻度を位置情報マネージャーに伝えます。 CLLocationManager には、更新の頻度など、位置情報データをフィルター処理および構成するためのオプションが多数用意されています。 この例では、位置情報が 1 メートル変わるたびに DesiredAccuracy が更新されるように設定します。 位置情報の更新頻度などの基本設定の構成の詳細については、Apple ドキュメントの「CLLocationManager クラス リファレンス」を参照してください。

  9. 最後に、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; }
    }
}

ユーザー インターフェイス

  1. Xcode Interface Builder を使用して、位置情報を表示する画面を作成します。 Main.storyboard ファイルをダブルクリックして開始します。

    ストーリーボード上で、位置情報のプレースホルダーとして機能するラベルをいくつか画面にドラッグします。 この例では、緯度、経度、高度、コース、速度のラベルがあります。

    詳細については、「Xcode を使用したユーザーインターフェイスの設計」を参照してください。

  2. 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
    

    これにより、アプリケーションの起動時に位置情報の更新が開始されますが、データは表示されません。

  3. 位置情報の更新を受信したので、位置情報を使用して画面を更新します。 次のメソッドは、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;

}

アプリケーションを実行すると、次のようになります。

アプリの実行例

アクティブおよびバックグラウンド状態の処理

  1. アプリケーションはフォアグラウンドでアクティブな間、位置情報の更新を出力します。 アプリがバックグラウンドに入るときに何が起こるかを示すために、アプリケーションの状態の変化を追跡する 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);
        }
    }
    
  2. コードには問題が 1 つ残っています。アプリがバックグラウンドにあるときに UI を更新しようとすると、iOS によってアプリが終了されます。 アプリがバックグラウンドに移行すると、コードは位置情報の更新のサブスクライブを解除し、UI の更新を停止する必要があります。

    iOS は、アプリが別のアプリケーションの状態に移行しようとしているときに通知を出します。 この場合、ObserveDidEnterBackground 通知をサブスクライブできます。

    次のコード スニペットは、通知を使用してビューに UI の更新を停止するタイミングを通知する方法を示しています。 これは ViewDidLoad に追加されます。

    UIApplication.Notifications.ObserveDidEnterBackground ((sender, args) => {
        Manager.LocationUpdated -= HandleLocationChanged;
    });
    

    アプリが実行されると、出力は次のようになります。

    コンソールでの場所の出力の例

  3. アプリケーションは、フォアグラウンドで動作しているときは画面に位置情報の更新を出力し、バックグラウンドで動作している間もアプリケーション出力ウィンドウにデータを出力し続けます。

未解決の問題が 1 つだけ残っています。アプリが最初に読み込まれたときに画面が UI の更新を開始しますが、アプリがいつフォアグラウンドに戻ったのかを知る方法がありません。 バックグラウンド アプリケーションがフォアグラウンドに戻っても、UI の更新が再開されません。

これを修正するには、アプリケーションがアクティブ状態になったときに発生する別の通知内に UI の更新を開始する呼び出しを入れ子にします。

UIApplication.Notifications.ObserveDidBecomeActive ((sender, args) => {
  Manager.LocationUpdated += HandleLocationChanged;
});

これで、アプリケーションの初回起動時に UI の更新が開始され、アプリがフォアグラウンドに戻るたびに更新が再開されます。

このチュートリアルでは、画面とアプリケーション出力ウィンドウの両方に位置情報データを出力する、適切に動作するバックグラウンド対応の iOS アプリケーションを作成しました。