5

widgetPerformUpdateWithCompletionHandler()Today Extension のコンテンツが変更されたかどうかを通知センターに知らせることができます。例えば:

func widgetPerformUpdateWithCompletionHandler(completionHandler: ((NCUpdateResult) -> Void)!) {
    // Refresh the widget's contents in preparation for a snapshot.
    // Call the completion handler block after the widget's contents have been
    // refreshed. Pass NCUpdateResultNoData to indicate that nothing has changed
    // or NCUpdateResultNewData to indicate that there is new data since the
    // last invocation of this method.

    if(has_new_data()) { // There was an update
        completionHandler(.NewData)
    }
    else { // nothing changed!
        completionHandler(.NoData)
    }
}

しかし、コンテンツが変更されたかどうかはどうすればわかりますか? すべてのスナップショットで、ウィジェットはゼロからインスタンス化されます。新しい PID を持つ完全な新しいプロセスです。したがって、インスタンスにプロパティを保存することはできません。現在のウィジェットのコンテンツを以前のスナップショットのコンテンツと比較するにはどうすればよいでしょうか?

Core Data を使用して、後で比較できるように現在のコンテンツを保存しました。これは明らかで機能します。しかし、別の問題が発生します。以前のスナップショットがない場合はどうなりますか? ユーザーが再びウィジェットを追加するためだけにウィジェットを削除したとします。または、ユーザーが再起動しました。今では考えられない以前のスナップショットがない理由は他にもあるかもしれません。いずれにせよ、Core Data にはまだコンテンツが保存されています。この古いコンテンツと現在のコンテンツを比較して変更がないことが検出.NoDataされた場合、通知センターはコンテンツを再描画しないため、ウィジェットは空になります。

completionHandler単に常に を返すのではなく、正しい状態でを呼び出すことがなぜ私にとって非常に重要なのか疑問に思われるかもしれません.NewData。これは、変化がなく、まだ戻っているときに少しちらつきが発生しているためです.NewData。ウィジェットにいくつかの画像があり、ウィジェットを再描画すると、コンテンツ全体がミリ秒間見えなくなります-気付くのに十分な時間です。

足りないものはありますか?Apple がさまざまな状態で応答するオプションを提供する方法を提供しているのに、どの状態に応答する必要があるかを検出することを不可能にしているのは、私には奇妙に思えます。

4

4 に答える 4

5

理論的には、これを使用して、最後の呼び出し以降に新しいコンテンツがあったかどうかを確認し、適切な値を返します。理論的には、複数の呼び出しでビュー コントローラーの同じインスタンスである可能性があると思いますが、それは明らかに現在の動作ではありません。コンテンツが変更されたかどうかの確認は、アプリと拡張機能の性質によって異なります。共有 Core Data ストアを使用してデータを共有しているため、次のようにすることができます。

  1. アプリがデータを変更するたびに、現在の日付を共有ユーザーのデフォルトに保存します。
  2. 今日の拡張機能がデータを読み取るたびに、現在の日付を共有ユーザーのデフォルトに保存します。
  3. widgetPerformUpdateWithCompletionHandlerが呼び出されたら、両方の日付を検索します。拡張機能が最後にそのデータを読み取ったときよりも最近データが変更された場合は、 を返しNCUpdateResultNewDataます。そうでない場合は、 を返しNCUpdateResultNoDataます。

これらの日付は、共有ユーザーのデフォルトではなく、永続ストアのメタデータに保存することもできます。毎回同じビュー コントローラーであった場合は、手順 2 の値をファイルに保存する代わりにメモリに保持することができます。しかし、これもまた現在の仕組みではなく、いつ変更されるかは明らかではありません。

他の方法でデータを保存するアプリは、別のチェックを使用する必要がある場合があります。その詳細は、アプリと拡張機能がどのように機能したかによって異なります。

iOS 8.2 の実際には、拡張環境はあなたが送り返す値を気にしないように見えるので、実際には問題ではありません。戻してみて、毎回戻すNCUpdateResultNewDataのと比べてみました。NCUpdateResultNoData今日の拡張ビュー コントローラーまたはそのビューのライフ サイクルにはまったく影響がありませんでした。

余談ですが、それは必ずしも異なるプロセスではありません。この行を今日の拡張機能に入れてみましたviewDidLoad

NSLog(@"viewDidLoad pid=%d self=%@", getpid(), self);

次に、今日の拡張機能を実行し、通知センターを数回上下にスクロールし、通知センターを閉じて再度開いたところ、次のようになりました。

2015-03-17 15:19:01.203 DemoToday[3484:903442] viewDidLoad pid=3484 self=<TodayViewController: 0x12d508cc0>
2015-03-17 15:19:14.441 DemoToday[3484:903442] viewDidLoad pid=3484 self=<TodayViewController: 0x12d50ade0>
2015-03-17 15:19:23.784 DemoToday[3484:903442] viewDidLoad pid=3484 self=<TodayViewController: 0x12d619c40>
2015-03-17 15:19:29.015 DemoToday[3484:903442] viewDidLoad pid=3484 self=<TodayViewController: 0x12d50abe0>

ビュー コントローラのインスタンスは毎回異なりますが、いずれの場合も同じ pid です。

于 2015-03-17T21:34:11.543 に答える
2

ホスト アプリとのデータ共有を有効にしている限り、任意のストレージ API ( Core Data、NSCoding、SQLite ) を使用できます。

https://developer.apple.com/library/prerelease/ios/documentation/General/Conceptual/ExtensibilityPG/ExtensionScenarios.html

ユーザーがウィジェットを削除したり、デバイスを再起動したりしても、データは共有コンテナーに保存されます。iOS がウィジェットを起動するたびに、以前に使用したものと同じ共有コンテナーを読み書きできるようになります。

ホスト アプリとウィジェットが同時に読み取りまたは書き込みを行う可能性があるため、読み取りと書き込みの操作を調整する必要があることに注意してください。

于 2015-03-14T14:55:29.097 に答える
1

これを行う最も簡単な方法は、現在のウィジェットの状態をユーザーのデフォルトにキャッシュすることです。次に、ウィジェットが (viewDidLoad で) 読み込まれると、キャッシュされたデータをユーザーのデフォルトから取得し、ウィジェット ビュー コントローラーのプロパティとして設定します。

次に、widgetPerformUpdateWithCompletionHandler が呼び出されると、以前の更新があり、データが十分に新しいかどうかのネットワーク リクエストを行う必要があるかどうかを判断できます。Web リクエストを実行すると、それをキャッシュと比較して、新しいデータがあるか更新がないかを判断できます。

于 2015-03-16T22:01:33.277 に答える
0

UserDefaults や Core Storage にデータを永続化する必要はありません。シンプルに保ちます。計算されたコンテンツを格納するために使用する変数、または何かが静的に変更されたかどうかを確認するために使用するデータを宣言するだけです。ウィジェットは同じプロセスで実行されるため (ここに示すように)、次にウィジェットがアクティブ化されたときに静的データが使用可能になります。

    static NSDate *lastDate;

    - (void)widgetPerformUpdateWithCompletionHandler:(void (^)(NCUpdateResult))completionHandler {
        NSDate * currentDate = [NSDate date];
        if (!lastDate ||
            [[NSCalendar currentCalendar]compareDate:lastDate toDate:currentDate toUnitGranularity:NSCalendarUnitDay] != NSOrderedSame) {
            // Date changed 
            lastDate = currentDate;
            ... do whatever needs to be done ...
            completionHandler(NCUpdateResultNewData);
        } else {
            completionHandler(NCUpdateResultNoData);
        }
   }
于 2016-08-18T08:55:27.493 に答える