ObjecTips

Swift & Objective-C で iOS とか macOS とか

Apple Watch Programming Guide まとめ その2

これの続き

Apple Watch Programming Guide: Developing for Apple Watch

2015-03-09 初出バージョンのドキュメントまとめ(網羅ではなく適宜省略)

WatchKit Apps

UI Essentials

Assembling Your Storyboard Scenes

Storyboard でのシーンの組み立て

  • UIの要素をシーンに追加すると縦にスタックされる
  • UIの要素を入れるコンテナの役割をする Group は重要な要素
  • Group はネストできる
  • Group は背景色か背景画像を設定できる
Accommodating Different Display Sizes

異なるディスプレイサイズへの対応

  • デフォルトではIB上で異なるサイズの Apple Watch のUIを全て同時に設定するが、個別に設定する事もできる
  • どのサイズの Apple Watch でも同じUIである事が望ましい
Updating Your Interface at Runtime

ランタイムでの値を設定と更新

  • 視覚的なアピアランスを変更する
  • サイズを変更する
  • 透明度を変更する
  • 表示、非表示する
  • ランタイムではオブジェクトの追加と削除、並びの変更はできない。hidden でレイアウトから一時的に取り除く事はできる
  • オブジェクトを隠すとトルツメされる
  • トルツメなしでオブジェクトを隠すには alpha を0にする
Setting Your App’s Key Color

Key Color について

  • Key Color はステータスバーのタイトルとショートルックの通知のアプリ名に適用される
  • Storyboard の Global Tint で設定する
Internationalizing Your Interface

ローカライズ

  • ローカライズは Storyboard の Base Internationalization の機能を使う
  • programmatically に指定するケースでは NSLocalizedString NSNumberFormatter NSDateFormatter などを使う
  • NSLocaleApple Watch の設定が使用される

Interface Navigation

画面遷移

  • 複数の画面を利用する際、ページベースのUIと階層的なUIはどちらかしか使えない
  • どちらもモーダルで表示できる
Implementing a Page-Based Interface

ページベースのUIの実装について

  • Storyboard で Interface Controller を置いて relationship を next page で接続するとページ化される

f:id:Koze:20150424150656p:plain

  • ページのセット(集合)は init メソッドなどで reloadRootControllersWithNames:contexts: メソッドを呼び出して変更する事ができる
  • 複数ページの中で最初に表示するページを指定するには initawakeWithContext:becomeCurrentPage メソッドを呼ぶ
Implementing a Hierarchical Interface

階層ベースのUIの実装について

  • programmatically に画面遷移を行うには pushControllerWithName:context: メソッドを呼ぶ。引数の context でデータを渡す事が推奨される
  • Storyboard ではボタン、グループ、テーブルの row から push segue を作成する

f:id:Koze:20150424151350p:plain

  • 遷移後の画面左上に「戻るボタン」が表示される。これをタップするか画面左端をスワイプする事で画面が閉じる
  • programmatically に閉じるには popController メソッドを呼ぶ
  • メインの Interface Controller は dismiss できない
Presenting Interface Controllers Modally

モーダルでの表示について

  • modal segue を作成する
  • presentControllerWithName:context: メソッドで単一の画面をモーダル表示
  • presentControllerWithNames:contexts: メソッドでページベースの複数画面をモーダル表示

f:id:Koze:20150424152125p:plain

  • モーダル表示の先がページベースになっていればページベースのコントローラをモーダル表示する

f:id:Koze:20150425032526p:plain

  • モーダル表示したコントローラの画面左上にはコントローラの title が表示される。title を Done や Close など任意に設定可能。title が設定されていない場合は Cancel と設定される

Interface Objects

インターフェースオブジェクト

  • インターフェースオブジェクトは WKInterfaceObject クラスやそのサブクラスのインスタンス
  • インターフェースオブジェクトは View ではない。Apple Watch上に実装されたUIの実際の View とワイヤレス通信をを行うプロキシオブジェクト
  • WatchKit ではコントローラからラベルなどに値を設定できるが、値や attributes の取得はできない
Creating an Interface Object

インターフェースオブジェクトの作成

  • ランタイムでインスタンスを allocate して initialize する事は無い
  • Storyboard に配置して作成する
Configuring Your Interface at Design Time

デザイン時のUIの設定

  • デザイン時に WKInterfaceLabel のテキスト、色、フォントなどを変更可能
Changing Your Interface at Runtime

ランタイムでのUIの変更

  • 初期化のタイミングも含めた Interface Controller が active なタイミングでのみインターフェースオブジェクトを設定可能
  • init awakeWithContext: willActivate メソッドとコントローラに設定されたアクションメソッドでラベルの値、画像その他のインターフェースの設定を行う
  • WKInterfaceController の初期化時にはまず super のイニシャライザを呼ぶ
  • インターフェースオブジェクトに同一の run loop で複数回値を設定したとしても、Apple Watch への送信は最適化され最新の値のみが送信される
  • 同一の値を同一のプロパティに送ると重複した呼び出しを発見するためのログメッセージが生成される
Responding to User Interactions

ユーザ操作への対応

WKInterfaceObject Action Method
Button - (IBAction)method
Switch - (IBAction)method:(BOOL)on
Slider - (IBAction)method:(float)value
Menu Item - (IBAction)method
Table table:didSelectRowAtIndex:
  • Segue が実行される前に WatchKit が contextForSegueWithIdentifier:inTable:rowIndex:contextsForSegueWithIdentifier:inTable:rowIndex: を呼び出す
  • ユーザ操作無しで画面を更新するなら NSTimer を使う
  • ネットワークアクセスや位置情報のモニタリングなどの時間のかかるタスクは親アプリで行うのがベスト。shared container を通じて WatchKit Extension にデータを戻す
Hiding Interface Objects

インターフェースオブジェクトを隠す

  • 表示したいオブジェクトは Storyboard ファイルに全て含まれていなければならない。表示内容をデータによってカスタマイズするにはオブジェクトを hide する。hide したオブジェクトはトルツメされる
  • setHidden:YES でオブジェクトを隠す

Text and Labels

Using Fonts

フォントの使用

  • グランスと通知ではシステムフォントのみ。アプリではカスタムフォントが使える
  • WatchKit App と WatchKit Extension のバンドルにフォントファイルをいれて WatchKit App の Info.plist に UIAppFonts キーを設定して追加したフォントファイルを指定する
  • カスタムフォントを使ってテキストを作成するには attributed string を使う。この時 attribute で指定されたフォントが存在しなければシステムフォントが使われる
Managing Text Input

テキストの入力

  • presentTextInputControllerWithSuggestions:allowedInputMode:completion: メソッドでテキストの選択肢表示、音声入力、絵文字入力、アニメーション絵文字入力の画面を表示する
Internationalizing Your Text Code

テキストのローカライズ

  • Storyboard の Base Internationalization の機能を使う
  • NSLocalizedString NSNumberFormatter NSDateFormatter を使う

Images

画像について

  • WKInterfaceImage クラスは単一の画像かシーケンスイメージをスタンドアロンのコンテンツとして表示する
  • WKInterfaceGroup WKInterfaceButton WKInterfaceController は他のコンテンツの背景画像として画像を表示する
Specifying Your Image Assets

画像アセットの指定

  • 可能なら常にPNGを使用する
  • 画像はサイズ変更ができないのでインターフェースオブジェクトを setWidth: setHeight: して画像が適切なサイズで表示されるようにする
  • image assets を使用する
Using Named Images to Improve Performance

名前付き画像の使用によるパフォーマンスの改善

  • setImageNamed: setBackgroundImageNamed: メソッドでバンドル内かデバイスにキャッシュ済みの画像を使用する
  • 画像データそのものを Apple Watch に転送するより、name の文字列だけ転送する方が時間も短くパワーも使わない
  • setImage: setImageData: setBackgroundImage: setBackgroundImageData: は WatchKit Extension から WatchKit App へ画像データをワイヤレスで転送する
  • Extension で UIImage を作成して使用する時は常に iPhone にある画像オブジェクトを使えるよう前もって Apple Watch にデータを送信しなければならない
  • imageNamed: メソッドを使ったとしても UIImage は WatchKit App ではなくて Extension のバンドルから読み込まれ、それを使用する際には Apple Watch にワイヤレスで画像データが転送される事になる
Caching Images on the Device

デバイスの画像キャッシュ

  • Extension で作成した画像を頻繁に使用するなら WKInterfaceDevice クラスの addCachedImage:name:addCachedImageWithData:name: メソッドでキャッシュする
  • WKInterfaceImagesetImageNamed:WKInterfaceGroupWKInterfaceButtonsetBackgroundImageNamed: でキャッシュする
  • アニメーション画像をキャッシュする場合は animatedImageWithImages:duration: メソッドで全てのアニメーションフレームを含んだ1つの UIImage を作成してキャッシュする。個々のフレームを個別にキャッシュしない事
  • キャッシュにはサイズの制限があって、それぞれアプリには約5MBのキャッシュ領域がある*1
  • キャッシュがいっぱいになったら新しいものを追加する前に既存の画像を removeCachedImageWithName:removeAllCachedImages で削除しなければならない

Tables

テーブルについて

  • シングルカラムの WKInterfaceTable
  • Storyboardではコントローラを追加して Interface Controller の IBOutlet を接続する
  • コードではそれぞれの row controller のクラスを定義して初期化時に row を追加する。ユーザによる row の選択にも適切に対応する
Configuring Row Controllers

Row Controllers の設定

  • content rows に対して異なる row controllers を使用できる
  • row controller には初期状態で1つのグループが入っていてそこにラベル、画像、その他オブジェクトを追加できる
    (Storyboard での設定方法は iOS の static tableView と同じ要領で、Storyboard上での階層構成は iOS での UITableViewCell が row controller、ContentView が group オブジェクトにあたる場所にある。ただし役割は iOS とは異なっていて row controller のクラスは NSObject になっている)
  • row controller は NSObject のサブクラスを作成して IBOutlet付きのプロパティでアウトレットを設定していく。また Storyboard にクラスを設定する際は identifier を設定する(このあたりも UITableViewCell と同じ感じ)
Configuring the Table’s Contents at Runtime

ランタイムでのテーブルのコンテンツの設定

  • setRowTypes:setNumberOfRows:withRowType: メソッドで row を作成して rowControllerAtIndex: で rowController を呼び出して値を設定する (ここはドキュメント内のサンプルコードを見た方が早い)
  • パフォーマンスのため row の数は20以下が良い。それ以上の場合は「続きを読み込む」のようなコントロールを提供する事を考慮する
  • 重要なデータのみを表示するのがベター
Handling Row Selections

Row の選択

  • row が選択されると Interface Controller の table:didSelectRowAtIndex: メソッドが呼ばれる。ここで新しい Interface Controller を呼び出したり row の内容を更新したりする
  • テーブル全体を選択不可にしたければ Storyboard上で Selectable オプションをオフに設定する

Context Menus

メニューについて

  • Force Touch で呼び出される Interface Controller に応じたメニューで最大4つまで表示可能
  • メニュー以外の場所をタップするとキャンセル扱いになる
Designing Your Menu Items

メニューのデザイン

  • 画像はテンプレート画像を用いる
Adding a Context Menu to an Interface Controller

メニューの設定方法について

  • Storyboard で既存の Interface Controller に設定、または programmatically に追加と削除が可能
  • 5つ以上設定可能だが表示されるのは4つまで
  • addMenuItemWithImage:title:action:addMenuItemWithImageNamed:title:action: メソッドで追加する
Handling Taps in a Menu Item

メニューの実行

  • メニューをタップするとメニュー画面が閉じて設定されたアクションが実行される

Settings

設定について

  • 設定は変更頻度の高くないアプリの振る舞いやアピアランスといったものを設定するのに使う。Settings Bundle はiOSアプリ内に入っていて iPhone内の Apple Watch アプリによって表示される
  • WatchKit の Settings Bundle は iOS の Settings Bundle と同じように動作する
Creating Your WatchKit Settings Bundle

WatchKit の Settings Bundle の作成

  • Xcode のメニューから Select File > New > File で Apple Watch > WatchKit Settings Bundle を選択し、Settings-Watch.bundle をiOSアプリのターゲットに追加。名前は変えてはいけない (WatchKit App や WatchKit Extension ではなくiOSアプリをターゲットにする事に注意)
Enabling Access to Preference Values for Your WatchKit Extension

Extension との設定項目の共有

  • shared group container を介してiOSアプリと WatchKit Extension間で設定を共有する
  • iOS Appと WatchKit Extension の両方で App Groups を有効にして同じ group identifier を選択する
  • Root.plistApplicationGroupContainerIdentifier キーを追加して同じ identifier を設定する
Accessing Settings at Runtime

ランタイムでの設定へのアクセス

  • NSUserDefaultsinitWithSuiteName: メソッドで初期化してグループコンテナにアクセスする

続く

*1:WatchKit 公開当初の公式ビデオだと20MBと言っていた