183
Help us understand the problem. What are the problem?

More than 1 year has passed since last update.

posted at

updated at

【iOS14】Widget(WidgetKit) まとめ

※この記事は2020/7/28時点のBeta3の情報を元に作成しています。

  • 2020/7/25 IntentConfigurationinitでplaceholderを引数に渡すことがdeprecatedになっていたので追記しました。
  • 2020/7/28 isPlaceholder(deprecated) redacted placeholder unredacted について追記しました。
  • 2020/8/21 TimelineProviderplaceholder(in:)、Location Permission、 Timelineの間隔、メモリの最大容量について追記しました。
  • 2020/8/27 Beta6でTimelineProviderのメソッド名や必須項目が変更になった件について追記しました。

iOS14から新しく登場した
WidgetKitを使ってWidgetを作成する機会があり
その際に調べたことや触ってみてわかったことをまとめました。

下記のWWDC2020のセッションを参考にしています。

Meet WidgetKit
https://developer.apple.com/videos/play/wwdc2020/10028/

Widgets Code-along, part 1: The adventure begins
https://developer.apple.com/videos/play/wwdc2020/10034/

Widgets Code-along, part 2: Alternate timelines
https://developer.apple.com/videos/play/wwdc2020/10035/

Widgets Code-along, part 3: Advancing timelines
https://developer.apple.com/videos/play/wwdc2020/10036/

Add configuration and intelligence to your widgets
https://developer.apple.com/videos/play/wwdc2020/10194/

Build SwiftUI views for widgets
https://developer.apple.com/videos/play/wwdc2020/10033/

Design great widgets
https://developer.apple.com/videos/play/wwdc2020/10103/

Widgetとは?

iOS13では
ホーム画面から左にスワイプすると出てくる
通知センターに設定できるViewで
TodayExtensionを利用して
Viewの表示や内容の更新したり
簡単なタスクを実行することができました。

image.png

それが
iOS14ではホーム画面にも表示ができるようになり
機能もさらに豊富になりました。

iPadやMacでも
同じように表示することができます。

widget.001.png

iOS14で登場したAppライブラリという機能で
アプリのアイコンをライブラリフォルダに
収納することができるようになったことで
ホーム画面をより自由にカスタマイズできるようになり
Widgetを使うことで
より必要な情報を手軽に確認できるようになりました。

スクリーンショット 2020-07-11 11.06.47.png

Widgetのゴール

WWDCのセッションの中で
下記の3点が
Widgetが目指すものとして掲げられています。

image.png

Glanceable

パッと見てすぐに欲しい情報が確認できる

というような意味です。

通常ユーザがホーム画面に止まるのは
ほんのわずかな時間です。

そのため
Widgetで何か操作を求めるなど
情報をパッと確認できること以外を行おうとするべきではない
と言っています。

Appleのアプリでは
下記のような情報が表示されるようになっています。

image.png

Relevant

ユーザの関心があるものを適切な時間に表示する

ということのようです。

例えば

  • 朝出かける前には今日の天気を知りたい
  • 日中の仕事中はタスクのTodoを忘れないようにリマインダーをチェックしたい
  • 寝る前にはリラックスできる音楽が聞きたい

などをWidgetでは実現することができます。

また
iPhoneの画面で
全てのWidgetを表示するのはサイズに限界があり
それを解消するために
スマートスタック
という新しい機能が追加されました。

スマートスタック

Widgetのコレクションを
一つのWidgetサイズの中に納め
システムが適切な時間に適切な表示をするように
自動で表示内容を変更できる機能です。


自身でスワイプして表示を変えたり
自動回転をオフにすることもできます。

最終的には
システムで何を表示するかの判断はされますが
開発側で何を優先的に表示させたいのかの設定もできます。

※詳細は後ほど記載します。

Personalize

使用しているユーザに合わせてカスタマイズできる

iOS14のWidgetでは
ユーザがWidgetに表示できる内容をカスタマイズできます。

例えば
天気アプリのWidgetで
自分が住んでいる地域の情報が知りたいとします。

そこでWidgetをタップすると
地域を選択できるリストが表示されます。

widget6.001.png

このように
ユーザが本当に必要としている情報が提供できる機会を
設けることが大切になります。

WidgetKit

こういったWidgetを実現するために
WidgetKitという新しいフレームワークが登場しました。

いくつかWidgetKitのコンセプトが紹介されています。

SwiftUIでマルチプラットホームを簡単に実現する

iOS、iPadOS、macOSどれでも同じ様に表示できるように
現時点でマルチプラットホームを実現する一番簡単な方法として
SwiftUIで実装するようになっています。

さらに
SwiftUIは
DynamicTypeやDarkModeへもほぼ自動で対応してくれます。

準備ができたら表示する

ユーザは
平均で1日90回ホームスクリーンを訪れるそうですが
そのほとんどが一瞬です。

その際に
ローディングインジケータがずらりと並んでいたら
なんの有益な情報も提供できません。

スクリーンショット 2020-07-12 10.26.58.png

そこでWidgetKitは
バックグラウンドでViewを生成して返すようにしています。

そして
Timelineという一つの時間軸の上で
適切なタイミングでViewを構築して表示します。

image.png

そしてこれをフレームワーク側で管理することで
この構築したViewを他の箇所でも再利用しています。

Widgetを追加する時
WidgetGalleryと呼ばれるモーダルの中でWidgetを選択します。

そこに表示されるViewは

  • 事前に用意したView

または

  • すでに構築が完了しているView

が表示されます。

スクリーンショット 2020-07-11 12.16.23.png

すでに構築されたViewを利用することで
実際にホーム画面に置いた時に
Widgetがどう表示されるかを
ユーザは確認することができます。

情報の更新ができる

Widgetを更新するためには

  • アプリからWidgetを更新する
  • 定期的なWidgetの更新をTimelineに設定する

という方法があります。

例えば
カレンダーアプリのWidgetでは
予定のタイミングでWidgetの表示が更新されるように
Timelineに設定をしています。

しかし
途中で予定が変わりアプリのスケジュールを変更したタイミングで
Widgetの更新も行われ
Timelineの設定が更新されます。

スクリーンショット 2020-07-11 12.30.18.png

スクリーンショット 2020-07-11 12.31.07.png

WidgetKitの使い方

ここからはWidgetKitについて見ていきます。

WidgetKitには下記のようなコンセプトがあります。

  • Kind
  • Configuration
  • SupportedFamilies
  • Placeholder

Kind

WidgetKitでは一つのextensionの中で
複数のWidgetを構築することができるようになっています。

スクリーンショット 2020-07-11 12.40.28.png

例えば
株価のアプリでは

  • 複数の株価の概要を確認できるWidget
  • 一つの株価の詳細を確認できるWidget

があります。

widget2.001.png

Configuration

WidgetKitには

  • StaticConfiguration
  • IntentConfiguration

があります。

StaticConfiguration

ユーザがカスタマイズする必要がないものなどに使用します。

例えば
Fitnessアプリの場合
すでにパーソナライズされた情報が表示され
ユーザが設定を変更する必要がないため
StaticConfigurationを使用しているようです。

スクリーンショット 2020-07-11 12.52.16.png

IntentConfiguration

ユーザが設定を変更することができます。

widget3.001.png

Listの部分をタップすると選択可能な一覧が表示され
Widgetに表示される値の種類が変更できます。

これはSiriKit intent definitionファイルを定義することで
固定の選択肢を提供することができます。

さらに
iOS14の新機能
in-app Intent handling
を使って動的に選択肢を作成することもできます。

SupportedFamilies

WidgetKitでは
3種類のサイズのWidgetを作成できます。

  • systemSmall
  • systemMedium
  • systemLarge

デフォルトでは全てのサイズをサポートしています。

image.png

Placeholder

実装する際に必須のViewの一つで

端末の設定の変更時に利用される(文字サイズ変更など)時に
呼ばれるとセッションでは言っていましたが

ほとんど使われることはなく
このタイミングで呼ばれるという保証はない
とも言っています。

ポイントとしては
このWidgetが何を表示するものなのかを表す
ことが大事だそうです。

そして
ユーザデータは含めずデフォルトの内容を含める
ことが推奨されていました。

実装例

ここからはコードを見ていきます。


開発を行うにあたって
Xcode12 Beta1では
シミュレータでWidgetが利用できません(Widgetのリストに表示されない)のでご注意ください。
Beta2では表示されるようになりました。

まずはWidgetを定義します。

image.png

上記で紹介した
KindやPlaceholder、
実際にデータを表示するViewを提供するクロージャを引数に
Configurationを作成しています。

Providerは具体的にViewを提供する方法を定義します。
具体的には後に登場するコードをご覧ください。

下記のような構成になっています。

WidgetKit-Architecture@2x.png

Viewを構築するときのポイント

ここから
より詳細にViewを提供するコードを見ていきますが
その前にViewを構築していく際のポイントについて見てみます。

下記の3つのWidgetは
必要最低限な情報を表示しており
タップするとアプリを開いて詳細画面へ遷移します。

スクリーンショット 2020-07-11 14.58.13.png

大事なのは

状態を持たないUI

を構築することです。

Widgetでは
スクロールや動画の再生などの操作はできません。

これは上記でも記載した
ユーザがホーム画面にいる時間の短さなどによる特徴だと考えられます。

その代わりに
Widgetをタップすることで
アプリにDeep Linkすることができます。

image.png

systemSmallはWidget全体で一つのリンクになっており
タップするとアプリへ直接遷移します。

systemMediumsystemLargeでは
個々のViewにwidgetURLというメソッドを使うことで
別々のリンクを設定してアプリに遷移することができます。

スクリーンショット 2020-07-11 15.16.21.png

スクリーンショット 2020-07-11 15.15.57.png

そしてアプリ側ではonOpenURLメソッドを使って
Deep Linkをハンドリングできます。

ドキュメントトップ
https://developer.apple.com/documentation/widgetkit


リンク先への遷移に失敗すると
アプリのトップページに遷移しました。

また
LazyVStackなど
LazyなViewと一緒に使用している場合に
画面に表示されていなければ
対象のリンク先へ遷移されません。

Viewがレンダリングされていないので
当然と言われればそうなのですが
最初「あれ?」と思ったので記録として書き残しておきます。

Viewを提供するコード

それでは
コードを見ていきます。

Viewを提供する箇所は3つあります。

  • Placeholder
  • Snapshot
  • Timeline

Placeholderについては
すでに見ましたので
下の2つについて見ていきます。

Snapshot

システムが
今すぐに1つのViewを必要としている時に
提供するViewです。

スクリーンショット 2020-07-11 15.38.24.png

ただし
これはダミーデータではなく
実際のWidgetで利用するデータです。

なので
SnapshotとTimelineの最初のViewは
多くの場合で同じになります。

同時に
WidgetGalleyに表示されるViewになり
ユーザがWidgetを追加した時に
最初に表示されるViewにもなります。

Timeline

複数のViewと日付の組み合わせで
いつどのViewをWidgetに表示したいか
を設定します。

スクリーンショット 2020-07-11 15.48.24.png

こうすると

フレームワークが
Viewをシリアライズ化してディスクに保存し
必要な時になったらレンダリングを行う

という仕組みになっているようです。

image.png

Timelineには
一般的には1日分の内容を設定するべきだそうですが
場合によってはより更新が必要なものもあります。

その場合
Reloads
と呼ばれるコンセプトに基づいて
表示の更新が実行されます。


ドキュメントにも詳細が記載されていますのでそちらをご参照ください。
https://developer.apple.com/documentation/widgetkit/keeping-a-widget-up-to-date

Reloads

フレームワークが
デバイスにあるそれぞれのWidgetを起動し
新しいTimelineを提供するように要求します。

Reloadsを行うことで
ユーザに最新の情報を提供していることを保証できます。

image.png


Reloadsに関しては
ドキュメントにも詳しい記載があるのでそちらもご参照ください
https://developer.apple.com/documentation/widgetkit/keeping-a-widget-up-to-date

具体的にはTimelineProviderを利用します。

スクリーンショット 2020-07-11 16.00.57.png

主に日付を持ったTimelineEntryがあり
https://developer.apple.com/documentation/widgetkit/timelineentry

Contextにはその時のデバイスの情報などが含まれています。

  • systemSmall or systemMedium or systemLarge
  • Widgetのサイズや位置
  • Widgetが表示される際に設定されている全てのenvironmentの情報
  • WidgetがWidgetGalleryに表示されるかどうか

例えば
Widgetを表示するのに
外部からリソースを取得することが必要な場合
WidgetGalleryに表示されるまでに時間がかかったり
エラーが発生すると表示されないことがあります。
(その場合真っ黒な画面になります。)

そこで
isPreviewメソッドを活用してtrueの場合には
staticなデータを返却するなどの対策を行うことができます。

snapshot

snapshotメソッドでは
1つのTimelineEntry

timeline

timelineメソッドでは
複数のTimelineEntryTimelinePolicyを添付した
Timeline

返します。

下記が具体的な定義方法の例です。

スクリーンショット 2020-07-11 16.20.53.png

TimelinePolicy

TimelinePolicyには3種類あります。

image.png

atEnd

提供した最後のEntryが表示された際に
フレームワークにreloadを依頼します。

after(date: Date)

指定した日時に
フレームワークにreloadを依頼します。

never

reloadをしないようにフレームワークに依頼します。

下記はドキュメントのイメージで
どのようにreloadが発生するのかが示されています。

最初にProviderから
3つのTimelineEntryを持ち
atEndをRefresh Policyに設定したTimelineを提供しています。

そうすると
最後のTimelineEntryが表示されたタイミングで
フレームワークがreloadを呼ぶことで
再びProviderから
新しいTimelineを提供しています。
neverをRefresh Policyに設定しているため
表示はずっと変わらなくなります。

image.png

注意点

ただし注意しなければいけない点としては
この設定通りには動かない可能性があります。

ReloadsはTimelinePolicyを基に
どのくらいWidgetが見られているかや
システムの状態を考慮して
最終的にフレームワーク側でreloadのタイミングを決めます。

また
システムの時間が急に大きく変わった場合などに
強制的にreloadする場合もあります。


Betaだからかもしれませんが
このタイミングはかなりバラつきがあり
どういうタイミングで発生するのかが
いまいちつかめませんでした。

(2020/8/21追記)
下記のForumの内容によると
最低でも15分程度の間隔は空ける必要があるようです。
https://developer.apple.com/forums/thread/653265

(2020/8/27追記)
Beta6でProtocolに変更がありました。

  • メソッド名がsnapshot -> getSnapshot
  • メソッド名がtimeline -> getTimeline
  • Contextの引数ラベルがwith -> in
  • placeholderメソッドが必須に

アプリからも更新できる

上記では
フレームワークにreloadのタイミングを委ねる方法でしたが
アプリからWidgetを明示的に更新する方法もあります。

例えば
アプリがバックグラウンド通知を受け取った時や
アプリ内でユーザがデータを更新した場合などが
考えられます。

image.png

WidgetCenterを使用します。

アプリに紐づいている特定のWidgetのみを更新したり
全てのWidgetを更新することもできます。
また現在のConfigurationを知ることもできます。

スクリーンショット 2020-07-11 16.41.32.png

注意点

ただし何でもかんでも更新するのではなく
Widgetに表示する内容に関連する更新が行われた際にのみ
更新するように考慮する必要があると言っていました。

バックグラウンドのURLSessionからデータを受け取る

Timelineを構築する際に
サーバからデータを取得する場合があると思います。

その際にバックグラウンドでAPIの呼び出しを行い
onBackgroundURLSessionEvents(matching:_:)
でリクエストの結果を受け取ることもできます。


ドキュメントやセッションでは言及されていますが
実際に試したところイベントを受け取ることができませんでした。
サンプルでも記載がないのでまだわかっておらず
引き続き調べています。

もしご存知の方いらっしゃいましたら
教えていただけましら幸いです🙇🏻‍♂️

注意点

ただし
こちらもリクエスト数なども考慮する必要があります。

Widget用に複数のリクエストを1つにまとめるなどの
対策が必要になるかもしれません。

アプリがバックグラウンドで動いている際のreloadのタイミングは
システムに委ねられます。

Reloadsを行う際には

プロセスやリクエストを効率的に行うこと。
Widgetは毎秒単位のずっと動いているようなものではありません。

Widgetの性質によって
どのくらいのReloadsが必要になるかを見積もり
考慮した設計をしましょう。

PersonalizationとIntelligence

Widgetをよりユーザに使いやすくするための
主に2つのコンセプトがあります。

  • Intents
  • Relevance

Intents

Intens.frameworkを利用して
ユーザにどのようにWidgetを表示するかの
選択肢を提供できます。

すでにSiriとShortcutsを一緒に利用する際に
活用されていた技術が
iOS14ではWidgetでも利用できるようになりました。

widget3.001.png


以下で簡単に実装方法を記載していますが
詳細に関してはドキュメントをご参照ください。
https://developer.apple.com/documentation/widgetkit/making-a-configurable-widget

Intent Definitionファイルの生成

File > New File and select SiriKit Intent Definition File
から作成できます。

※ドキュメントの図です。
image.png

ファイルを作成すると内部で
この内容にそったクラスが自動で生成されます。
CoreDataのxcdatamodeldと似たような仕組みのようです。

コード

IntentConfiguration
IntentTimelineProviderを活用します。

スクリーンショット 2020-07-11 17.23.17.png

スクリーンショット 2020-07-11 17.25.18.png

https://developer.apple.com/documentation/widgetkit/intentconfiguration
https://developer.apple.com/documentation/widgetkit/intenttimelineprovider

Intents Extensionを使って動的に選択肢を生成する

in-app Intent handling
というiOS14の新機能を使って
動的に選択肢を生成することもできます。

その場合
Intents Extensionをターゲットに追加し
IntentHandlerを実装する必要があります。

下記のような形で実装します。


class IntentHandler: INExtension, SelectCharacterIntentHandling {
    func provideCharacterOptionsCollection(for intent: SelectCharacterIntent, with completion: @escaping (INObjectCollection<GameCharacter>?, Error?) -> Void) {

        // Iterate the available characters, creating
        // a GameCharacter for each one.
        let characters: [GameCharacter] = CharacterDetail.availableCharacters.map { character in
            let gameCharacter = GameCharacter(
                identifier: character.name,
                display: character.name
            )
            gameCharacter.name = character.name
            return gameCharacter
        }

        // Create a collection with the array of characters.
        let collection = INObjectCollection(items: characters)

        // Call the completion handler, passing the collection.
        completion(collection, nil)
    }
}

そしてIntentTimelineProviderの中で
結果を使用します。


struct CharacterDetailWidget: Widget {
    var body: some WidgetConfiguration {
        IntentConfiguration(
            kind: "com.mygame.character-detail",
            intent: SelectCharacterIntent.self,
            provider: CharacterDetailProvider(),
            placeholder: CharacterPlaceholderView()
        ) { entry in
            CharacterDetailView(entry: entry)
        }
        .configurationDisplayName("Character Details")
        .description("Displays a character's health and other details")
        .supportedFamilies([.systemSmall, .systemMedium, .systemLarge])
    }
}


このinitはXcode12 Beta3でdeprecatedになりました。
placeholderを引数に渡さないようです。


struct CharacterDetailProvider: IntentTimelineProvider {
    func timeline(for configuration: SelectCharacterIntent, with context: Context, completion: @escaping (Timeline<CharacterDetailEntry>) -> ()) {
        // Access the customized properties of the intent.
        let characterDetail = lookupCharacterDetail(for: configuration.character.name)

        // Construct a timeline entry for the current date, and include the character details.
        let entry = CharacterDetailEntry(date: Date(), detail: characterDetail)

        // Create the timeline and call the completion handler. The .never reload 
        // policy indicates that the containing app will use WidgetCenter methods 
        // to reload the widget's timeline when the details change.
        let timeline = Timeline(entries: [entry], policy: .never)
        completion(timeline)
    }
}

代わりにisPlaceholderメソッドや
https://developer.apple.com/documentation/swiftui/view/isplaceholder(_:)

redactedメソッドとplaceholderプロパティを組み合わせて
Placeholderを作成するようです。

https://developer.apple.com/documentation/swiftui/anyview/redacted(reason:)
https://developer.apple.com/documentation/widgetkit/timelineprovider/3656451-placeholder?changes=latest_beta

※ 2020/8/1追記
isPlaceholderはdeprecatedになっておりredactedを使用するようです。

Appleのサンプルコード内では下記のように利用していました。


RewardsCardEntryView(entry: .init(date: Date(), points: 4))
    .previewContext(WidgetPreviewContext(family: .systemMedium))
    .redacted(reason: .placeholder)

さらに
@EnvironmentredactionReasonが取得できるので
この値が空ではないとき(=現状では.placeholderが指定されているとき)に
placeholder表示に変更するということができます。


@Environment(\.redactionReasons) var redactionReasons

また逆にunredactedを利用することで
一部のViewのみをPlaceholder表示にしないこともできます。

※ Beta3ですとImageはplaceholderになりませんでした。
次のアップデートで改善されるのではないかと思っています。

※ (2020/8/21追記)
Beta5で新しくplaceholder(in:)TimelineProviderに追加されました。
上記のplaceholder(in:)はBeta3か4で追加されていたようです。
これを使用するとViewを自動でredactedが適用されるようです。
https://developer.apple.com/documentation/widgetkit/timelineprovider/placeholder(in:)-6ypjs

Relevance

冒頭の方でも紹介しましたスマートスタックの中で
システムは合理的にユーザが関心が高いであろう
Widgetを表示するようにします。

その際の評価方法をアプリやWidgetを通じて
変更することができます。

主に2つの方法があります。

  • アプリ内のユーザの操作からShortcutsにdonationする
  • WidgetのTimelineEntryTimelineEntryRelevanceを設定する

Shortcutsにdonation

ユーザの操作をINIntentとして
donationしておくことで
同じINIntentだと判断された際に
スタックの中のWidgetが表示される可能性が高まります。

image.png

下記の例では

  • 使用したカード
  • 購入した商品カテゴリー

を表示するWidgetですが

月曜日と火曜日に
同じカードで同じ商品カテゴリーのものを購入したアクションを
donationしておきます。

そうすると
金曜日に同じような操作をした際に
その購入履歴のWidgetが表示されやすくなります。

image.png

TimelineEntryにTimelineEntryRelevanceを設定

TimelineEntryには
TimelineEntryRelevanceを使って
重みをつけることができます。

image.png

image.png

scorefloat
これまでに提供した全てのEntryとの比較値として利用されます。

ドキュメントにも下記のような記載があります。

The score is a value 
you choose that indicates the relevance of the widget, 
relative to entries across timelines that the provider creates.

例えば
下記のように購入した金額をscoreに設定することで
購入金額の高いものが優先的に表示されるようになります。

image.png

duration
設定されたEntryが
Widgetに表示する時間になってから
どのくらいそのscoreが有効であるかを示す値です。

例えば
下記の例はバスケットボールの試合のスコアを表示するWidgetです。

試合の開始時にscore加え
試合が終了するまでの3時間(duration = 3hrs)を設定しています。
以降がnil(設定なし)なのは
Relevanceに影響を与えないことを示しています。

一つ前で0としているのは
次のRelevanceが来るまでは
このscore(= 0)を継続して使用することを示しています。

スクリーンショット 2020-07-11 18.14.19.png

良いWidgetを設計するために

ここからは良いWidgetを設計するためのポイントを
セッションの内容から見てみたいと思います。

Design great widgets(優れたWidgetの設計)のセッションの中で
大きく2つに分けて紹介されていました。

  • Ideation(何を表示するのか)
  • Creation(どう表示するのか)


Creationに関しては
HIG(Human Interface Guideline)と内容が似ていましたので
ドキュメントをご参照して頂けましたら幸いです。

Ideation

大きく分けて3つについて
述べられています。

  • Principles
  • Editing
  • Multiples

Principles

大事なポイントとして以下の3点が挙げられています。

スクリーンショット 2020-07-12 5.11.29.png

Personal

よりユーザに特化した情報を提供することで
アプリとユーザの感情的な繋がりを強くする。

Information

多くの情報の中から
一番重要な情報の概要を選んで提供する。

Contextual

ユーザがアプリで繰り返し操作して得ているような情報(必要としている情報)
を提供する。

必要な情報は時と場合によって異なるので
それに応じた情報が提供できるようにする。

カレンダーアプリは下記のようにスケジュールに応じて
表示方法を変えています。

一番重要な情報として
直近のスケジュールの内容と時刻と場所を表示します。

また
スケジュールの量や祝日に合わせて表示方法を変えたり
連絡先から知り合いの誕生日を表示することができます。

widget4.001.png

天気アプリでは
例えば雷雨が発生している場合は
より細かい分ごとの降水量を表示して
何時ごろに雨が止むのかが確認できるようになります。

widget5.001.png

Editing

iOS14の新機能で
ユーザがWidgetに設定できる内容を
カスタマイズできます。

これはWidgetにどのように情報を表示するのかに
関わってきます。

冒頭の方でも紹介しましたが
天気アプリでは表示する地域を選択できます。

widget6.001.png

さらに同じ種類の複数のWidgetを表示して
他の地域の天気を表示することもできます。

image.png

これが実現できることで
1つのWidgetに複雑な情報を詰め込む必要はなく
シンプルでわかりやすい表示を可能にします。

Multiples

1つのアプリの中で
複数の種類のWidgetを作成することも可能です。

例えば
株価アプリでは
ウォッチリストのサマリーを表示するWidgetと
一つの株価を細かく表示するWidgetが
用意されています。

どういった情報をユーザがすぐにチェックしたいかどうかを考え
複数のWidgetを用意するべきかどうかを検討します。

スクリーンショット 2020-07-12 9.07.33.png

Widget作成に役立つView

最後にセッションの中で紹介されていた
Widget作成において役に立つViewを2つ紹介します。

Text init(_:style:)

SwiftUIのテキストに新しいイニシャライザが追加され
DateからTextを生成できるようになりました。


init(_ date: Date, style: Text.DateStyle)

これは指定した時間からの経過時間を自動で表示してくれます。
Widgetは常時動いているものではありませんが
このTextを活用することで
リアルタイムに起動しているように見せることができます。

※セッションの画像です。

スクリーンショット 2020-07-12 9.44.11.png

またString Interpolationを活用することで
表示のローカライズも可能です。

スクリーンショット 2020-07-12 9.45.50.png

ContainerRelativeShape

HIGにも記載されていますが
WidgetのCorner Radiusと
コンテンツのCorner Radiusは
一致させることが推奨されています。

しかし
この値はデバイスサイズに変わってくるため
手動で計算するのは大変難しいです。

スクリーンショット 2020-07-12 9.49.18.png

そこで今回
ContainerRelativeShapeが追加されました。

これは親ViewのCorner Radiusに合わせるように
フレームワーク側で自動で計算してくれるようになります。

Beta5よりLocation Permissionが必要に

(2020/8/21追記)

WidgetでLocationを使用する場合に
Permissionをユーザに求めることが必要になりました。

そのため
Info.plistに下記のKeyが必要になります。

  • NSLocationUsageDescription
  • NSWidgetWantsLocation

NSWidgetWantsLocation
https://developer.apple.com/documentation/bundleresources/information_property_list/nswidgetwantslocation

メモリの最大容量

ドキュメントに記載はありませんが
こちらの記事によると30MBを超えるとクラッシュするようです。

まとめ

iOS14のWidgetについて見てきました。
これまでのアプリのアイコンが並んだホーム画面から
よりユーザが必要としている情報をすぐに確認できるようになるのは
便利そうだなと感じています。

一方で
必要ないものはユーザからどんどん遠ざかっていくのではないかとも感じており
Widgetを使うことでユーザに利便性を高めていくことは
大事になってくるのだろうと思いました。

まだまだ不明な点が多く
実際にReloadの(timelineメソッドが呼ばれる)タイミングで
通知音を出して確認してみましたが
予定の時間に更新がされなかったり
予期せぬタイミングで更新されたりします。


ロック画面から復帰したら鳴ったり
他のアプリ削除したら鳴ったり
ウィジェットを追加していなくても鳴ったり...など

ただ使い続けていると
Timelineに設定した内容に関しては
だんだん時間通りに通知が来るようになってきたなとも感じています。
アプリの使用頻度も関わっているようなので
それが影響しているのかなと思っています。

データの更新とViewの更新のタイミングも
必ずしも一致しているわけではありませんでした。

また
スマートスタックを利用した時に
どのくらいの頻度で自分のWidgetが表示されるのか
Relevanceの有効性がどのくらいなのかなど
まだまだわからないことがあるので
これからも触ってみて色々調べていきたいなと思います😃

何か間違いなどございましたら
教えていただけますと助かります🙇🏻‍♂️

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
183
Help us understand the problem. What are the problem?