iOS 7 キーカラーの落とし穴

MacDev

iOS 6 以前は、それぞれの UI 部品の挙動をユーザに想像&認識させるために光源と陰/影の再現による立体的な描写が用いられてきた。押せるものは立体的に膨らんでいるし、手前にある物体の背後には影がある。
これは現実世界における現象と同じで、人間の脳にもともと備わっている形状認識機能を活かすことができ、とても自然で合理的なやり方である。Mac OS や Windows など、PC の世界でも長いこと使われてきた手法。

iOS 7 では方針が大幅に変更された。ほとんどの UI 部品からは立体表現が消え、単純な線や塗りつぶしのみで表現されるようになった。ボタンなんて枠すらついていない。UI 部品がすっきりすればユーザがコンテンツに集中できるようになるはずだと。
でもそれだけではコンテンツ表示部と操作可能な部分の区別がつかないので、アイブ率いる新 UI チームが取った手法はキーカラー。App 内で一つのカラーを決めておき、ボタンのように押せるものはそれで塗りつぶす。人間が何かを探すときまず初めに認識するのは色なので、どこにボタンがあるのか、一瞬でわかる。

でも、この方法って無理がある。1 つのカラーに特定の役割を持たせるならコンテンツ内で似たようなカラーを使うとユーザの混乱の原因になるし、キーカラーの選択も難しい:

1. 鮮やかで濃いキーカラー
→ コンテンツよりも目立って邪魔になる(コンテンツと同列な見た目なので余計に)
2. 鮮やかで薄いキーカラー
→ 読みにくい、見にくい(iOS 7 は細いラインが多いので余計に)
3. くすんだキーカラー
→ ラベルと区別がつきにくくなる
4. 薄いキーカラー
→ 非アクティブ(Disabled 状態)に見えてしまう
5. ラベルのカラーと同じキーカラー
→ だめ!

上記の問題を解決しないままリリースされている App がいくつもある。特に 4 と 5 はよくない。5 をやるんだったら、せめて Windows Phone みたいに徹底的に ○ で囲むとか、何か区別をつけないと。

Mac アプリケーションが個性を出すべきではない部分

MacDev

Reeder が独自タイトルバーを採用しているように、ここ数年 Human Interface Guidelines を一部無視して作られたアプリケーションが人気になっているし、“The HIG is dead”みたいな話もときどき目にする。

確かに HIG はあらゆるタイプのアプリケーションが違和感なく共存できるように考えられていて、アプリケーションによっては無駄を感じられる部分もある。

それでも、いくつかの要素に関しては変えてしまうと個性というより違和感になってしまうので、個人的に気になるものをまとめてみる。

細かいことばかりに思われるかもしれないし、「こんなの UI デザインの本質ではない」とか言う人もいるかもしれない。だけどこういう“細かいひっかかり”だって積もれば山になるし、そんな部分が比較的少ないことが自分が Mac を使う理由でもある。

スクリーン座標と UI 部品のレイアウト

Mac では昔からウインドウ内の UI 部品が、左上から右下にかけてだいたい次のように並んでいる:

↖ [削除] [閉じる] [キャンセル] [前へ] [次へ] [完了] ↘

これが統一されている意味は大きく、ダイアログでキャンセルしたければテキストを確認しなくても自然にカーソルを左に動かすし、作業を次に進めたければ右下を探す。アプリケーションに慣れる時間を大幅に短縮できる。

記号とその意味

機械に慣れていない人でも CD プレーヤーを操作できるのは、▶ を押せば再生、止めるときには ■ を押せばいいと誰もが知っているからである。Mac の UI においても、同じ意味に対しては共通の記号を用いるべきだと思う。

Apple は履歴を移動するボタンを三角 [◀ | ▶] で統一しているし、そうでない移動(iPhoto など)には矢印 [← | →] が使われることが多い。

メニューを表示するボタンは ▼ をつけるべきという話があるけど、Apple 製アプリケーションも含め、つけないものが増えているのが残念。

UI における表記

記号と同じ。すべての項目を選択したいとき、メニューから“すべてを選択”を探す。これがアプリケーションによって“一括選択”とか“全部選択する”のようにバラバラだったら、探すのに時間がかかるだけで何のメリットもない。Windows には Windows の、OS X には OS X のための文字列を用意するべきである。

Mac アプリケーションの日本語リソースにおける法則については Mac Apps + Japanese というページにまとめてあるので、参考にどうぞ。

ユーザの操作に対する移動量

ユーザがトラックパッドやマウスで行った操作がどのように反映されるかは非常に重要で、プラットフォームの操作感を大きく左右する部分でもある。

ありがたいことにマウスカーソルの移動は完全にシステムが管理しているため、どのアプリケーションでも同じ。この速度や加速度がバラバラだったらと思うとぞっとする。

しかしピンチジェスチャとか 2 本指スクロールをしたときの移動量が違うアプリケーションはいくつかあり、非常に気持ち悪い(例:Photoshop CS6)。Photoshop は CS3 では自然なスクロールだったのになぜ変えてしまったんだ!

光源

ウインドウの影を見ればわかるように、OS 9 までは左上にあった光源が OS X では中心ちょっと上に移動している。グラデーションやドロップシャドウを見れば、メニューバーからボタンやアイコンまで、スクリーン上のすべてが共通の光源を持っているのがわかる。

全体の光源を無視して影を右下に置くようなアプリケーションは安っぽく見えてしまうので注意。

OS X 10.8 で改善された Layer Backed View とテキストレンダリング

MacDev

以前 NSView (AppKit) の CALayer 対応について、テキストのサブピクセルレンダリングが上手く機能しない問題があるという記事を書いた。Sleipnir for Mac の開発ブログ記事でもこの問題に触れられている。

復習すると、

  • 古くからの描画方式では通常 1 つのウインドウにつき 1 枚のグラフィックスコンテキストがある。
  • layer-backing を有効にするとそれぞれの NSView がグラフィックスコンテキストを持つようになる。
  • サブピクセルレンダリングは背景の色に依存。必ず背景の上に描画しないといけないので Layer-backed view とは相性が悪い。

...しかし、あれは Lion の時点での話。

Mountain Lion での改善

10.8 の AppKit Release Note にはこんなことが書かれている:

NSTextField has been updated to allow LCD font smoothing to work when the view is layer-backed. Prior to 10.8, NSTextField would directly draw the text into the layer's contents; this would cause text to render incorrectly due to the LCD font smoothing algorithm not having adjacent pixels to smooth fonts with. Layer-backed applications that manually draw text should move to using NSTextField to get proper LCD font smoothing.

自分の理解:

  • NSTextField がアップデートして layer-backed でもサブピクセルレンダリングできるようになった!
  • これまで NSTextField の描画内容は、ほかの NSView サブクラスと同様、layercontents に 1 枚のビットマップ画像として設定されていた。これでは描画するときにその背景を知りようがないので正しくサブピクセルレンダリングできない。
  • そこで 10.8 以降は何か特別な仕組みで描画するようになったみたい。
  • 今後は、自作ビュー内に自力で描画していたテキストも NSTextField に任せるといいらしい。

続きを読むとこれが機能するには条件があって:

One caveat is that NSTextField does require an ancestor which is layer-backed and opaque; it does not have to be the direct parent view, but some view in the ancestor chain must be opaque for this to be turned on and work correctly.

NSTextFieldsuperview をたどっていったとき、先祖のどこかで「layer-backed かつ opaque」なビューが必要らしい。

実験してみよう

いくつかの疑問:

  • 本当に機能するの?
  • 10.7 との違いは?
  • 裏にあるビューの中で色が変化しても大丈夫?

まず、NSWindow を 1 つ用意。contentView の中に NSTextFieldSomeView を配置。

  • contentView:ウインドウ全体を覆うビュー。
    • 背景を [NSColor windowBackgroundColor] で塗りつぶす。
  • SomeView:適当な図形を表示するビュー。
    • -isOpaque で YES を返す。
    • 2 つ用意。上には NSTextField の兄弟として、下には NSTextField の親として配置。

結果 1:Layer なし

AppKit の昔からある描画方式。

言うまでもなくサブピクセルレンダリングは正しく機能している。これが理想。

結果 2:OS X 10.7.5 での結果

これ以降は contentView が layer-backed である。

字がふにゃふにゃしていて汚い。

結果 3:OS X 10.8.2 での結果

Release Note に書かれていた通りだ。

  • NSTextFieldSomeView が兄弟関係にある上では効いていない。
  • 親子関係にある下ではしっかりサブピクセルレンダリングされている。

サブピクセルレンダリングが効いていない部分(上)も Lion(結果 2)より奇麗。

結果 4

おまけ。contentView を opaque にしてみた。

これで全体にサブピクセルレンダリングが効くようだ。結果 1 との違いもないのではないか。

結論

NSViewCALayer による layer-backing をサポートしていても使うのをあきらめてしまう原因の一つだったけど、Mountain Lion で大きく改善されたようだ。ちょっとした制約はあるけれども。

Sandbox に悩まされることばかり

MacDev

6 月から Mac App Store のアプリケーションは Sandbox 化が必須になったけど、最近アプリケーションを作っていて悩むのはほとんどその Sandbox 関係だ。

PubSub.framework

RSS のフィードを扱いやすくしてくれる、Leopard で追加されたフレームワーク。今のところ Safari や Mail で使われている。

Lion になっても 1 つの PubSubAgent プロセスを複数アプリケーションで共有する仕組みが維持されていて Sandbox と相性が悪そうだ。と考えていたら本当に動かなかった。

しばらく調べて“Sandboxing the PubSub framework”という記事を発見。知りたいことがすべて書いてある。.entitlements ファイルの中身が temporary-exception だらけになってしまうのが悲しいけれども。

このフレームワーク、MobileMe のときは複数 Mac で未読フラグなどを同期してくれたのだけど、iCloud になってからは同期されなくなってしまったのが残念。最近は目立った改良もなく Apple もあまり力を入れていないようなので、この先どうなるのか将来性が不安。
それでもほとんどコードを書かずに RSS の取得/構文解析/登録を行うことができる優れものなのだ。

2013.12.8 追記:PubSub は Mavericks で見事に deprecated!自分が使わなくなったフレームワークは廃止する、それが Apple。

ログイン項目に登録 ヘルパーをログイン時に開く

多くのアプリケーションでは“ログイン時に開く”というチェックボックスがあってそれをクリックするだけでログイン項目の登録/削除ができるのだけど、Sandbox ではこれまでの LSSharedFileListInsertItemURL() を使った方法が使用できない。

解決策は Stack Overflow の“How to create a helper application for Mac App to start it on user login?”という質問に書いてある、SMLoginItemSetEnabled() を使った方法。
ただしアプリケーション自身を登録することはできず内包するデーモンのみ登録可能で、これまでのようにシステム環境設定の“ログイン項目”のところには表示されないという違いがある。

  1. Info.plist に LSUIElement を追加したヘルパーアプリケーションを用意。
  2. ヘルパーは親アプリケーションパッケージ内の Contents/Library/LoginItems/ に置くようにする。
  3. 親アプリケーション:LSRegisterURL() でヘルパーの URL を登録。
  4. 親アプリケーション:SMLoginItemSetEnabled() にヘルパーのバンドル ID を渡す。

この方法で開かれるのはヘルパーアプリケーションなので、これまでの代替にならない場合もある。「ヘルパーアプリケーションの中で親アプリケーションを開けばいいのでは?」と考えたが、Sandbox に弾かれてしまう。

アプリケーションを再度開く

自動アップデートの仕組みを提供する Sparkle で新バージョンをダウンロードしたあとにインストール・終了してから再度開く仕組みがあったけど、あれはアプリケーションを起動するためだけのプロセスを用意しておき、それを起動してからアプリケーションを終了することで実現している。

Sandbox を有効にすると、用意したプロセスの起動までは成功。だが“ログイン時に開く”の件と同様、そのプロセスから親アプリケーションを開くところで弾かれてしまう。

成功例を探したところ、LaunchAtLoginHelperというものを発見。ソースを読んでみると...

  1. 親アプリケーションの Info.plist で独自の URL スキームを定義しておく。
  2. ヘルパーでそれを使用した URL を開き、親アプリケーションを起動。

なんと URL スキームを使っていた。Apple が推奨しているのかは知らないけど iOS ではよく見かける。こんなところも Back to the Mac なのか...

UIKit for Mac はいつ?

MacDev

Retina ディスプレイが登場していろいろメリットはあるけど、そのうち重要な一つが、サブピクセルレンダリングなしでも文字が読みやすくなることだと思う。

“試用可能な場合は LCD で滑らかな文字を使用”というチェックボックスがシステム環境設定にあるけど、これがサブピクセルレンダリングに関する設定。

↑サブピクセルレンダリングによる違いとその説明を兼ねた図。通常のアンチエイリアス(上)では線の太さをグレーで表現していて、細く小さい文字だとピクセルが足りず擦れたようになってしまう部分があるけど、サブピクセルレンダリング(下)ではテキストカラーとは異なる色が少し混じっていて上記の原理でこの問題を解決している。

サブピクセルレンダリングには一つの大きな制約がある。それは、背景の色に依存していて必ず背景の上に描画しないといけないことだ。

AppKit の古くからの方式では 1 つのウインドウにつき 1 枚のグラフィックスコンテキストがあり、再描画する必要のある領域を親ビューから子ビューへと順番に描画して書き換えていく方式なのでサブピクセルレンダリングとは相性がいい。

iPhone では登場当時からサブピクセルレンダリングがないけど、基本的に Mac より文字のサイズ(ピクセル数)が大きくボールドが多用されていたこともありほとんど読みにくいことはなかった。iPhone 4 以降では Retina ディスプレイが採用され、通常のアンチエイリアスでも細い線までインクのように美しい。

iOS の UIKit は Core Animation の CALayer ベースでそれぞれのビューが独立したグラフィックスコンテキストを持ち、それらにあらかじめ描画しておいたものを GPU が移動や変形をするため滑らかなアニメーションが実現できるのだけど、これに加えてサブピクセルレンダリングを使用しようとすると

  • LCD 1 ドットの中で RGB の並ぶ方向に依存するため、ビューを描画する時点で画面に対する向きを知っている必要がある。もし画面が回転するようなことがあればすべての文字を再描画しなければならない。
  • 1 つのコンテキスト内で背景と文字を描画する必要があるため UILabel のようなビューではその背後のビューと同じ色の背景色を設定しなければいけなかったりして手間がかかる。
  • UILabel の背景にあるビューがグラデーションだったらどうする?

といったように、CALayer のメリットを打ち消してしまう。

なので UIKit のような CALayer ベースの UI Kit を Mac にも欲しいという声がときどきあるけど、そのメリットを最大限に活かすにはサブピクセルレンダリングを捨てなければならず、登場は Mac 全体に Retina ディスプレイが浸透してからではないかなと思う。

2012.6.17 追記

でも AppKit は NeXT 時代から続く歴史の長いフレームワークであるとはいえ、互換性を重視しすぎる他社のものと比べると数多くの脱皮を繰り返し、しっかりと時代に合った進化をしてきた部分も多い。

OS バージョンアップのたび徐々に CALayer のサポートが強化されたり、Lion でまったく新しい自動レイアウト機能が NSView に導入されたりするのを見ると、Apple もまだ本気で AppKit に力を入れているような感じもする。

Mac と AppKit はまだまだ長い付き合いかもしれない。

2017.11.23 追記

Mountain Lion からはサブピクセルレンダリングが効くようになった!(→ 2013.3.2

フイルタじゃない

MacDev

Mac Apps + Japanese に「母音がウの文字に続くイは大文字」なんて書いたけど、“フィルタ”は違うな。どう書けばいいんだろう。

Mac Apps + Japanese

MacDev

早速、DocumentsMac Apps + Japanese のページを追加。
これは以前から作ろうと思っていた、Mac アプリケーションの日本語化に関する文書だ。

ユーザは気にしなくてもいいけど、アプリケーションを提供する側に立つ人は細かい部分までこだわるべきだと思う。

(あまり気にしない開発者が多いのか、iOS アプリケーションの日本語表記はひどすぎるので自分は iOS デバイスをすべて英語環境にして使っている。)