A Day In The Life

とあるプログラマの備忘録

Autolayout に対応した画面で UIScrollView を使う時のコツ

iOS 6から導入された Autolayout(オートレイアウト)を最近になって本格的に使うようになりました。
4-inch の iPhone が主流になりつつある中、アプリを Autolayout に対応させるのはほぼ必須といっても良いと思います。Storyboard と格闘してなんとかコツはつかめたものの UIScrollView だけは一筋縄ではいかず苦労しました。
というわけで Autolayout に対応した画面で UIScrollView を使う時のコツをまとめてみました。
なおこの記事で説明する内容は Storyboard 上で Autolayout がオンになっていることが前提になっています。
Autolayoutをオンにする

Autolayout 対応画面で UIScrollView を使うための2種類の方法

Autolayout に対応した画面で UIScrollView を使うには以下の2つの方法があります。

  • Mixed Approach
    Autolayout と AutoresizingMask(iOS 6以前からあるレイアウト方法)を組み合わせる方法
  • Pure Auto Layout Approach
    Autolayout だけを使う方法

どちらのアプローチで実装するにしても UIScrollView オブジェクトは Autolayout を使って配置します。違いは UIScrollView オブジェクトの中に配置するビュー(Content View といいます)を AutoresizingMask を使って配置するか Autolayout を使って配置するかです。
Mixed Approach と Pure Auto Layout Approach に関する詳細は以下のアップル公式ドキュメントを参考にしてください。

サンプルアプリの概要

以下のサンプルアプリを実装しながら説明していきます。
アプリのイメージ
画面の中央に 320 × 170 サイズの UIScrollView オブジェクトを配置して、その中に UIView オブジェクトを2つ横に並べています。表示範囲を横スクロールさせるアプリです。

前準備

それぞれの実装方法を説明する前に準備として UIScrollView オブジェクトを配置しておきます。

UIScrollView オブジェクトの配置

Storyboard を使って、320 * 170サイズの UIScrollView オブジェクトを配置します。
UIScrollView を配置する

Constraints を追加

配置した UIScrollView オブジェクトを選択した状態で Constraints を追加します。まずはじめに UIScrollView オブジェクトの大きさを固定します。
大きさを固定する
次に UIScrollView オブジェクトを画面の真ん中にくるように設定します。
真ん中に設定する

プロパティを追加して IBOutlet 接続する

以下のように UIViewController の子クラス(ここでは仮に ViewController クラスとします)に UIScrollView 型のプロパティを追加します。

@interface ViewController : UIViewController

@property (nonatomic, strong) IBOutlet UIScrollView *scrollView;

@end

Storyboard で追加したプロパティを IBOutlet 接続します。
以上で前準備は完了です。それでは両アプローチの実装方法を見ていきましょう。

Mixed Approach を使って実装する

Autolayout と AutoresizingMaskを組み合わせる方法です。UIScrollView オブジェクトを Autolayout で配置して、スクロールビューの中に配置するビューを Autolayout を使わずに配置します。
以下のように viewDidLoad メソッドを実装します。

@implementation ViewController

- (void)viewDidLoad
{
  [super viewDidLoad];

  /*
   * ナビゲーションコントローラを使って
   * ビューコントローラを管理している場合は下記設定が必要
   */
  self.automaticallyAdjustsScrollViewInsets = NO;

  // 1つ目のビューのインスタンス生成
  UIView *view1 = [[UIView alloc] initWithFrame:CGRectMake(40, 0, 240, 170)];

  // 2つ目のビューのインスタンス生成
  UIView *view2 = [[UIView alloc] initWithFrame:CGRectMake(300, 0, 240, 170)];

  // スクロールビューにビューを追加する
  [self.scrollView addSubview:view1];
  [self.scrollView addSubview:view2];

  // スクロールビューのコンテンツサイズを設定する
  self.scrollView.contentSize = CGSizeMake(580, 170);
}

@end

UIScrollView オブジェクトにコンテントビュー(view1 と view2)を配置して contentSize を設定するおなじみ方法です。注意点としてはコメントにも書いた通り、ビューコントローラがナビゲーションコントローラーに管理されている場合、ビューコントローラの automaticallyAdjustsScrollViewInsets プロパティに NO を設定しないとコンテントビューの位置がずれてしまうことです(モーダルビューの場合はこの設定は不要です)。どうやら iOS 7 特有の問題のようです。詳しくはこちらの記事を参照してください。

Pure Auto Layout Approach を使って実装する

純粋に Autolayout だけを使って実装する方法です。Mixed Approach に比べると少しコードが長くなる傾向にあります。Autolayout は出来る限り Storyboard を使って設定したい所ではありますが UIScrollView に関して想定通りに動いたためしがないのでコードで書くことにします。
以下のように viewDidLoad メソッドを実装します。

@implementation ViewController

- (void)viewDidLoad
{
  [super viewDidLoad];
    
  /*
   * ナビゲーションコントローラを使って
   * ビューコントローラを管理している場合は下記設定が必要
   */
  self.automaticallyAdjustsScrollViewInsets = NO;
    
  // 1つ目のビューのインスタンス生成
  UIView *view1 = [[UIView alloc] init];
    
  // 2つ目のビューのインスタンス生成
  UIView *view2 = [[UIView alloc] init];
    
  // スクロールビューにビューを追加する
  [self.scrollView addSubview:view1];
  [self.scrollView addSubview:view2];
    
  // AutoresizingMaskをオフにする
  self.scrollView.translatesAutoresizingMaskIntoConstraints = NO;
  view1.translatesAutoresizingMaskIntoConstraints = NO;
  view2.translatesAutoresizingMaskIntoConstraints = NO;
    
  // Constraint(制約)を追加
  [self.scrollView addConstraints:
   [NSLayoutConstraint constraintsWithVisualFormat:@"|-40-[view1(==240)]-20-[view2(==240)]-40-|"
                                             options:0
                                             metrics:0
                                               views:NSDictionaryOfVariableBindings(view1, view2)]];
  [self.scrollView addConstraints:
   [NSLayoutConstraint constraintsWithVisualFormat:@"V:|-0-[view1(==170)]"
                                             options:0
                                             metrics:0
                                               views:NSDictionaryOfVariableBindings(view1)]];
  [self.scrollView addConstraints:
   [NSLayoutConstraint constraintsWithVisualFormat:@"V:|-0-[view2(==170)]"
                                             options:0
                                             metrics:0
                                               views:NSDictionaryOfVariableBindings(view2)]];
}

@end

UIScrollView オブジェクトの contentSize はAutolayout によって自動で計算されるため設定する必要がありません。また UIScrollView オブジェクトとその中に配置するコンテントビューの translatesAutoresizingMaskIntoConstraints プロパティはすべて NO に設定する必要があります。

制約を記述する Visual Format Language

Autolayout ではビューの位置を Constraint(制約)を使って指定します。Constraint を指定するには NSLayoutConstraint の constraintsWithVisualFormat:options:metrics:views: メソッドを使います。このメソッドの第1引数には Visual Format Language と呼ばれる特殊な文字列を渡します。サンプルコード上の1つ目の Visual Format である |-40-[view1(==240)]-20-[view2(==240)]-40-| を図に表すと以下のようになります。
Visual Formatの意味
左端からマージンを40あけて1つ目のビューを240の幅で配置し、さらにマージンを20あけてから2つ目のビューを240の幅で配置、最後に右端から2つ目のビューまで40あけるという意味になります。
残り2つの Visual Format の V:|-0-[view1(==170)]V:|-0-[view2(==170)] は指定しているビューが違うだけで意味は同じです。図に表すと以下のようになります。
Visual Formatの意味その2
縦位置のマージンは0、ビューの高さは170という意味になります。「V」はおそらく Vertical の V です。

参考

Autolayout と UIScrollView についての詳細は以下の Apple 公式ドキュメントを参考にしてください。

UIViewController クラスの automaticallyAdjustsScrollViewInsets プロパティについては以下の記事を参考にしてください

Autolayout の基礎的な内容については以下の記事が参考になりました。

Autolayout と UIScrollView についての Stackoverflow の議論

サンプルソース

この記事で使用したサンプルプログラムをこちらに置いておきます。

iPhone/iPadアプリケーション開発の教科書が増刷されました。

皆様ご購入ありがとうございます。
このたび「プロの力を身につける iPhone/iPadアプリケーション開発の教科書」が増刷されました。

本当にありがとうございます。めちゃくちゃうれしいです。
増刷分では以下の記事に掲載した訂正箇所が修正されています。

書店さんによってばらつきがあると思いますがそろそろ増刷分が店頭に並ぶと思います。
今後ともよろしくお願いします。

2015年1月19日追記

Swiftに対応した改訂版が発売されました。よろしくお願いします。

ジェネラティブ・アートが面白い

久しぶりに Processing でコード書いてます。なんでかと言うと、たまたま本屋さんで見つけた「ジェネラティブ・アート」という本がめちゃくちゃ面白くてそれがきっかけです。

個人的にはChapter 6の「創発」、Chapter 7の「自律性」、Chapter 8の「フラクタル」が特に面白かったです。創発とか自律性の考え方は AI っぽくてゲーム開発にも活かせそうでした。
「ジェネラティブ・アート」は拙著「プロの力を身につける iPhone/iPadアプリケーション開発の教科書」の序章でもプログラミング初心者に最適な本として紹介しています。
プログラミングの楽しさやプロラムが動いたときの感動がダイレクトにわかる良書です。本に掲載されているサンプルプログラムを入力して動かした時に「おお〜」「なるほど!」みたいなわくわくする感じ、ここ最近なかった感覚でした。やっぱり Processing は面白いです。

サンプルプログラムをリファクタリングしてみました

「ジェネラティブ・アート」の最終章「フラクタル」にサトクリフ五角形を作るサンプルがあったのですが、少しプログラムの効率が悪かったので勝手にリファクタリングしてみました。
ちなみにサトクリフ五角形とは数学者であるアラン・サトクリフ氏が考案した五角形です。以下のような五角形のことを指します。

まず五角形を描いて、その 5 つの辺のそれぞれの中点から垂線を引き、この垂 線上の点を結べば別の五角形を作れると言っています。こうすれば、形状の残りの部分も同じ く、さらなる五角形に分けられるので、結局それぞれの五角形がさらに 6 個のサブ五角形に分 けられます。サブ五角形の内にはさらにサブサブ五角形ができるので、この分割を無限小に向 かって繰り返すことができます。

ジェネラティブ・アート 219ページ
再帰的な五角形のコード

まずは227ページの再帰的な五角形(図8.14)から修正してみます。

FractalRoot pentagon;
int _maxlevels = 5;
float _strutFactor = 0.2;
int _numVertex = 5;

void setup() {
  background(255);
  size(1000, 1000);
  smooth();
  pentagon = new FractalRoot();
  pentagon.drawShape();
}
/*
 * 元のサンプルコードではPointObjという名前でした。
 * クラス名変更しました。
 */
class Point {
  float x, y;
  Point(float ex, float why) {
    x = ex; y = why;
  }
}
/*
 * サンプルコードをそのまま利用する
 */
class FractalRoot {
  Point[] points = new Point[_numVertex];
  Branch rootBranch;
  
  FractalRoot() {
    float centX = width/2;
    float centY = height/2;
    int count = 0;
    for (int i = 0; i < 360; i += 360/_numVertex) {
      float x = centX + (400 * cos(radians(i)));
      float y = centY + (400 * sin(radians(i)));
      points[count] = new Point(x, y);
      count++;
    }
    rootBranch = new Branch(0, 0, points);
  }
  
  void drawShape() {
    rootBranch.drawMe();
  }
}

class Branch {
  int level, num;
  // 頂点
  Point[] vertices;
  
  Branch(int lev, int n, Point[] points) {
    level = lev;
    num =  n;
    vertices = points;
  }
  
  void drawMe() {
    int deg = 0;
    int numVertex = vertices.length;
    Point[] projPoints = new Point[numVertex];
    for (int i = 0; i < numVertex; i++) {
      strokeWeight(5 - level);
      int nexti = i + 1;
      if (nexti == numVertex) {
        nexti = 0;
      }
      // 五角形の一辺を引く
      Point point = vertices[i];
      Point nextPoint = vertices[nexti];
      line(point.x, point.y, nextPoint.x, nextPoint.y);
      // 中点を計算する
      strokeWeight(0.5);
      fill(255, 150);
      Point mid = calcMidPoint(point, nextPoint);
      ellipse(mid.x, mid.y, 5, 5);
      // 中点から垂線を引く
      int destination = i+3;
      if (destination >= numVertex) {
        destination -= numVertex;
      }
      Point proj = calcProjPoint(mid, vertices[destination]);
      line(mid.x, mid.y, proj.x, proj.y);
      ellipse(proj.x, proj.y, 5, 5);
      projPoints[i] = proj;
    }
    
    strokeWeight(0.5);
    fill(255, 150);
    if ((level + 1) < _maxlevels) {
      // 垂線の先から新たに五角形を描く
      Branch childBranch = new Branch(level + 1, 0, projPoints);
      childBranch.drawMe();
    }
  }
  /*
   * サンプルコードをそのまま利用する
   */
  Point calcMidPoint(Point end1, Point end2) {
    :
    : 省略
    :
    return new Point(mx, my);
  }
  Point calcProjPoint(Point mp, Point op) {
    :
    : 省略
    :
    return new Point(px, py);
  }
}

もとのコードは Branch クラスが outerPoints, midPoints, projPoints, myBranches という名前の Point 配列をメンバ変数として保持していました。さすがに配列4つもいらないので1つの配列で管理するように改善しました。
calcMidPoint メソッドと calcProjPoint メソッドの処理は省略しました。処理の中身が気になる方は「ジェネラティブ・アート」を購入して確認してみてください。
出力結果は以下のようになります。
再帰的な五角形の出力結果

完全なサトクリフ五角形のコード

再帰的な五角形のコードをもとに完全なサトクリフ五角形を描画します。修正するのは Branch クラスの drawMe メソッドのみです。

class Branch {
  :
  : 省略
  :
  void drawMe() {
    strokeWeight(5 - level);
    int deg = 0;
    int numVertex = vertices.length;
    Point[] midPoints = new Point[numVertex];
    Point[] projPoints = new Point[numVertex];
    for (int i = 0; i < numVertex; i++) {
      int nexti = i + 1;
      if (nexti == numVertex) {
        nexti = 0;
      }
      // 五角形の一辺を引く
      Point point = vertices[i];
      Point nextPoint = vertices[nexti];
      line(point.x, point.y, nextPoint.x, nextPoint.y);
      Point mid = calcMidPoint(point, nextPoint);
      midPoints[i] = mid;
      int destination = i+3;
      if (destination >= numVertex) {
        destination -= numVertex;
      }
      Point proj = calcProjPoint(mid, vertices[destination]);
      projPoints[i] = proj;
    }
    
    if ((level + 1) < _maxlevels) {
      Branch childBranch = new Branch(level + 1, 0, projPoints);
      childBranch.drawMe();
      for (int i = 0; i < numVertex; i++) {
        int nexti = i-1;
        if (nexti < 0) { nexti += numVertex; }
        Point[] newPoints = { projPoints[i], midPoints[i], vertices[i],
                                  midPoints[nexti], projPoints[nexti] };
        Branch branch = new Branch(level+1, i+1, newPoints);
        branch.drawMe();
      }
    }
  }
  :
  : 省略
  :
}

出力結果は以下のようになります。
サトクリフ五角形の出力結果

ポスチュアとユーザインターフェースから見る iOS アプリのトレンド ー iOS7 の登場で変わるアプリのトレンド ー

最近読んだアラン・クーパー氏の著書『About Face 3』ではソフトウェアを「使われ方」と「見た目」の2つの視点で分類していました。拙著『プロの力を身につける iPhone/iPadアプリケーション開発の教科書』の第3章では iOS アプリを実装面から分類しました。実装面からの分類は UI 設計時には役に立ちますがアプリの方向性やターゲットユーザを決めるような UX 設計時にはあまり役に立ちません。UX 設計のようにアプリをマクロの視点から考えて設計するときには『About Face 3』で紹介されている分類のように実装に依存しないユーザ目線から分析したものの方が使えます。というわけでこの記事では『About Face 3』のソフトエェアのパターンを iOS アプリにあてはめて最近のアプリの傾向を分析してみます。

About Face 3におけるソフトウェアのパターン

『About Face 3』では以下のように、ソフトウェアを「使われ方」と「見た目」の2つの視点から分類しています。

使われ方の違いによるパターン

使われ方の違いによるパターンは『About Face 3』の中ではポスチュアパターンとして紹介されています。聞き慣れない単語ですが、ポスチュアについては以下のように説明されています。

製品のポスチュアとは、振る舞いのスタンスであり、ユーザーに対して自分をどのように見せるかということだ。ポスチュアは、ユーザが製品とのインタラクションにどのくらいの注意を払うか、ユーザーの注意の程度に応じて製品がどのように振る舞うかという問題である。

製品全体とユーザとの距離を決めるのを助けてくれるパターンです。ポスチュアには以下の3つのパターンがあります。

  • デーモン的なポスチュア
    ユーザの気づかないところで動いているソフトウェア。通常ユーザとの間にインタラクションは発生しない。プリンタドライバやネットワーク接続、サーバからの Push 通知など
  • 単発的なポスチュア
    ごくわずかのコントロールで1つの機能をサポートし、すぐに通り過ぎてしまうソフトウェア。必要に応じて起動され、ユーザが目的を達成するとすぐに閉じられる。天気予報アプリや音楽再生アプリ、マップアプリなど
  • 支配者的なポスチュア
    ユーザの注意を長時間に渡って独占するソフトウェア。ユーザはソフトウェアを持続的に起動状態に保つことが多い。電子メールアプリやプレゼンテーション作成アプリ、音楽制作アプリなど

単発的なポスチュアと支配者的なポスチュアはターゲットとするユーザの傾向がちがいます。表にすると以下のようになります。

表1:単発的なポスチュアと支配者的なポスチュアの比較

見た目の違いによる分類

見た目の違いによる分類は『About Face 3』の中ではユーザインターフェースのパラダイムとして紹介されています。ソフトウェアの見た目には以下の3つのパラダイムがあります。

  • 実装中心のインターフェース
    ソフトウェアがどのように作られているか、どのように組み立てられているかをそのまま表現しているインターフェース。ユーザはソフトウェアの内部的な仕組みやプログラムの動かし方を理解していないと使えない
  • メタファ(metaphor)的なインターフェース
    インンターフェースに含まれているビジュアルな手がかりとその機能をユーザが直感的に結びつけてくれることを頼りとするインターフェース。スキュアモーフィック(skeuomorphic)デザイン*1を採用しているソフトウェアに使用されることが多い。iOS6までのアップル純正アプリはスキュアモーフィックデザインをベースにメタファを巧みに取り入れたものが多い
  • イディオム(idiom)的なインターフェース
    ユーザがイディオムを学んで使うメカニズムを基礎としているインターフェース。実装中心やメタファ的なインターフェースと違い技術的な知識や機能の直感的な理解に依存しない。イディオムとは慣用句や熟語という意味の単語でここでいうイディオムとは単純な操作を組み合わせて意味のある操作を定義することを指します。わかりやすい例では、マウスのダブルクリックや右クリックに特定の動作を割り当てたり(ダブルクリックでアプリが起動する、右クリックでコンテキストメニューが表示される)、スマホのジェスチャー(ダブルタップや画面長押し、フリック)に特定の動作を割り当てるなどがあります。イディオムを操作の中心にしたソフトウェアではメタファがかえってユーザの学習を妨げることがあるため、リアルな見た目とは相性が悪い

近年、直感的にわかるソフトウェアがユーザの為によいとされる傾向があり、メタファ中心のインターフェースを持ったソフトウェアが(特にスマートフォンでは)高い評価を得ていたように思います。『About Face 3』ではメタファの欠点として以下を挙げて、メタファに頼りすぎるソフトウェアに対して警告を発していました。

  • メタファは文化的な違いや世代の違いを超えれない
    とある国ではメタファになり得るものが別の国ではメタファにならない。またフロッピーディスクのアイコンや黒電話のアイコンのように現物を知っている世代にはメタファになるものが知らない世代にはメタファにならない(参考:保存アイコンでみえてくるアイコンデザインの勘違い)
  • 初心者から中級者になった後はメタファが操作の邪魔になる
    メタファは初めて使うユーザには小さな効果があるが、操作に慣れるとメタファを必要としなくなるためかえって邪魔になることがある
  • メタファは現実世界に縛り付けてしまいコンピュータ本来の力を発揮できない
    メタファを使うと現実世界の制限をそのままソフトウエアの世界に持ち込んでしまう
  • すべてをメタファで表すことは現実的に不可能
    メタファで表せるものとそうでないものがある

逆にイディオムは学習が必要ではあるものの、優れたイディオムは一度学習すれば身に付くため、優れたイディオムがあればメタファに頼る必要がないとありました。とくに支配者的ポスチュアのように頻繁に使うソフトウェアにはメタファ中心のインターフェースよりもイディオム中心のインタフェースの方が相性が良いと思います。

クーパー氏の分類を iOS アプリに当てはめると

『About Face 3』の分類はあくまでデスクトップアプリケーションのための分類なので iOS アプリにあてはめる場合には少しアレンジしてやる必要があります。使われ方のパターンにあるデーモン的ポスチュアはOSの機能であったりアプリの1機能として実装されることがほとんどで単体アプリとして存在することはあまりないため除外して、見た目のパターンにある実装中心のソフトウェアは開発者の未熟さの結果できるものなので除外して考えてみます。
この2つを除くと、単発的と支配者的ポスチュアとメタファ的とイディオム的インターフェースが残ります。
これらはそれぞれ対極的な意味をもっています。そこでメタファ的/イディオム的ソフトウェアと単発的/支配者的ポスチュアをそれぞれXY軸にしてアプリを分類してみます。

2013年7月現在の iOS アプリの傾向

私が独断と偏見で選んだ2013年7月現在のアップル純正アプリとその他開発会社のアプリをグラフに配置すると以下のようになります。

図1:アップル純正アプリとその他開発会社のアプリの傾向

アップルがメタファを多用したアプリを開発しているのに対して、Google がメタファに頼るのをやめてシンプルな見た目のアプリを開発している傾向があります。以下は表で取り上げたアプリの一覧です。

支配者的ポスチュア × イディオム中心

メールや SNS などコミュニケーション系アプリが多い印象です。スマートフォン向けの支配者的ポスチュアなアプリはまだまだ発展途上にあるようです。ただし Figure のような尖ったアプリもあります。iOS 7の登場でこのエリアのアプリがどのように発展するか要チェックです。

単発的ポスチュア × イディオム中心

とても挑戦的なアプリが多い印象です。単機能のアプリが多くチャレンジしやすいのかもしれません。

支配者的ポスチュア × メタファ中心

About Face 3では支配者的ポスチュアとメタファは相性が悪いとされています。また支配者的ポスチュアなアプリが全体的に少ないのでこのエリアのアプリもあまりありませんでした。AmpliTube のように現実世界を忠実にシミュレートするためアプリは今後も一定以上のニーズはありそうです。

単発的ポスチュア × メタファ中心

アップル純正のアプリがわかりやすかったので集めてみました。

  • メモ
    メモ帳アプリ、紙の質感やページめくりのメタファを使用して実物のメモ帳に似せています
  • カメラ
    カメラアプリ、カメラのレンズや絞りのメタファを使用しています
  • カレンダー
    カレンダーアプリ、紙の質感はないものの1ヶ月を1ページに配置する実物のカレンダーの配置を使用しています
  • マップ
    地図アプリ、紙の質感を使用しています
  • 天気
    天気予報アプリ、天気を表すアイコンが写実的です

フラットデザインの流行と iOS7

6月に発表された iOS7 は従来のメタファを多用したデザインをやめて見た目がとてもシンプルになりました(iOS7 の概要は以下の YouTube 映像で見ることが出来ます)。

iOSiPhone 発売以来はじめて大幅な刷新をするということで大きな話題になりニュースやブログで沢山の記事が書かれました。ただどの記事も見た目の話に終始していて「スキュアモーフィックデザイン VS フラットデザイン」という視点の内容が目立ちました。個人的にはスキュアモーフィックとフラットデザインは対極に語るものではないと考えています。見た目だけでなく、この記事で分類したようにユーザからどのように使われるのかという視点を入れてやることで少し違った感じ方が出来ると思います。
アップルは iOS7 で再びイノベーションを起こそうとしているように思います。単純に見た目を変えるだけでは終わらないと思います。個人的には成功するかどうかの鍵は、スキュアモーフィックデザインを捨てて革新的な OS になるのか、それともスキュアモーフィックデザインを捨てきれずに見た目だけシンプルになり、以前より使いにくくなってしまうのかだと考えています。

アップルがデザインを一新したのは、近年 iPhone に限らず AndroidWindows Phone などスマートフォンが当たり前になり、大半のユーザがスマホ特有の操作(タップやフリックなど)に慣れたことで、見た目のわかりやすさよりも毎日使っても飽きない実用性の高いアプリが求められるようになったのだと思います。シンプルな見た目でメタファに頼らないフラットデザインが流行している理由もこのあたりにあるのではないかと考えています。
AmpliTube のようにメタファを多用して現実世界を忠実に再現するのが目的のアプリは iOS7 になっても必要とされると思います。とはいえ全体的には下の図の示す方向に流行は徐々に変わっていくと思います。

トレンドの移行

まとめ

ここまでの内容をまとめると以下の3つが重要になります。

  • アプリを見た目(インターフェース)からだけ分類するのではなく、ユーザがアプリとどのように付き合うかという視点(ポスチュア)からも分類する
  • ポスチュアの違いがアプリの見た目に影響を与えることがある
    支配者的ポスチュアのソフトウェアはメタファが邪魔になる時がある
  • iOS7 の登場でメタファを多用したアプリよりもシンプルな見た目のアプリがトレンドになる

シンプルな見た目だからデザインが簡単というわけではなくむしろ抽象度が上がるので、デザインの難易度はスキュアモーフィックを前提にしたアプリを開発するよりも難しくなると思います。一歩間違うと「シンプル=何もない」になってしまいそうで非常に怖いです。また見た目をシンプルにしてジェスチャーを多用するアプリがユーザに受け入れられるのか、今のところよくわからない(Clear や Weathercube が必ずしも使いやすいわけではない)です。開発者としてチャレンジしがいはあるものの大変な時代に突入しそうだなぁと、今から少々ビビっています。逆に1アプリユーザの立場から考えると今後、革新的で便利なアプリが増えて面白くなりそうだなとわくわくしています。

*1:人工物からコピーされたデザイン。かならずしもリアルな見た目である必要はないので注意。時計の長針と短針をモチーフに使ったフラットな見た目のアプリや電卓のキー配列をそのままコピーしたフラットな見た目のアプリなどもスキュアモーフィックデザインに分類される

アフォーダンスとシグニファイア

最近デザイン系の本を読んでいて「アフォーダンス」という言葉の使われ方が変わってきたなと感じています。
アフォーダンスはD・A・ノーマン氏の著書「誰のためのデザイン」がきっかけに広まった言葉で、もとはアメリカの知覚心理学者のJ・J・ギブソン氏が提唱した概念です。Wikiによると環境が動物に与える意味のことをアフォーダンスと呼ぶようです。私自身はユーザをとある操作に誘導するための重要な概念だなぐらいの認識でした。
最近ではデザイン系、なかでもユーザインターフェースについて書かれたの本やブログで当たり前のように出てきていうように感じます。
私がアフォーダンスという言葉の使われ方が変わったなと感じたのは先の「誰のためのデザイン」を書いたD・A・ノーマン氏の別の著書「複雑さと共に暮らす」を読んだ時です。その中でノーマン氏はアフォーダンスという言葉が一人歩きして本来の意味から離れて使われていることに言及しています。またアフォーダンスにかわる新たな概念としてシグニファイアを新たに提唱していました。以下その引用です。

デザインの用語では、シグニファイアはしばしばアフォーダンス、より正確には「知覚されたアフォーダンス」と呼ばれる。これは私が「誰のためのデザイン?」で紹介した用語なのだが、申し訳ないことに、実際のところ私の失敗だった。アフォーダンスはシグナルという言葉が持つよりもずっと深い意味を持っている。アフォーダンスは必ずしも知覚可能である必要はない。「シグニファイア」という用語を導入するのは、デザインの用語をより正確にするためである。

複雑さと共に暮らす ーー 2.社会的シグニファイア 100ページ

他にもアラン・クーパー氏の著書「About Face 3」では人工物から直感的に知覚する性質を単なるアフォーダンスと使い分けてマニュアルアフォーダンスと呼んでいました。About Face 3ではノーマン氏の「誰のためのデザイン?」を引き合いにアフォーダンスについて以下のように説明しています。

ドナルド・ノーマンは独創的な著書「The Design of Everyday Things」(「誰のためのデザイン?ー認知科学者のデザイン原論」野島久雄訳、1990年新曜社)の中で、アフォーダンスという用語を作り出した。彼は、この言葉を「ものの見かけ上の性質と本当の性質、特に、ものがどのように使えるかを決める根本的な性質を指す」と定義している。
ーー中略ーー
私たちの目的では、ノーマンの定義は大切なことを1つ省略している。それは、それらの性質が私たちに与えてくれるものをどのようにして知るかということだ。何かを見て、その使い方を理解したとき、つまり、アフォーダンスを理解した時には、もとの使い方の結びつきを作るために何らかの手段を使ったはずなのだ。
そこで、私たちはノーマンの定義から「と本当の性質」というところを省略することを提案したい。
ーー中略ーー
しかし、話を明確にするために、手でものを操作する方法についての、このような本能的な理解については、マニュアルアフォーダンスと呼ぶことにしよう。手や足に合うような人工物を見ると、私たちはそれを直接操作できるものだと理解し、説明を書いて示す必要はなくなる。

About Face 3 ーー 13.4 マニュアルアフォーダンス 287ページ

シグニファイア、マニュアルアフォーダンスともに言葉自体はあまり広まっているようには感じませんが、アフォーダンスを本来の意味できちんと使おうする動きは徐々に広まっているように思います。
例えばつい最近発売されたばかりのLukas Mathis氏の著書「インタフェースデザインの実践教室」でもアフォーダンスについて以下のように知覚可能なアフォーダンスと説明していました。

アフォーダンス(affodance)はもともと「環境が(人間を含む)動物に対して与える(affordする)意味」を指す言葉ですが、「人間にとって知覚可能なデザイン上の手がかり」の意味で使われる場合もあります。ドナルド・ノーマンは「複雑さと共に暮らすーデザインの挑戦」では、「知覚可能なデザイン上の手がかり」の意味では「シグニファイア(signifier)」という言葉を使い、曖昧な使われ方をしている「アフォーダンス」という言葉は避けた方がよいとしています。この本では「アフォーダンス」という言葉を用いますが、曖昧にならないよう「知覚可能なアフォーダンス」などと記述します。

インタフェースデザインの実践教室 ーー 9.5.7 指針その7ーアフォーダンス 78ページ

上記のシグニファイア、マニュアルアフォーダンス、知覚可能なアフォーダンス、これらの意味はかなり近いと思います。同じと考えても良さそうです。シグニファイアとアフォーダンスの関係を図にすると以下のようになると思います。

アフォーダンスとシグニファイアの関係

私自身は後藤武氏、佐々木正人氏、深澤直人氏の著書「デザインの生態学」を読んだ時にはじめて知ったくちで、何となくわかった気になって文章を読んだり使っていました。アフォーダンスとシグニファイア意識して使いたいなと思いました。

参考書籍

この記事で紹介した本を出版順に時系列に並べると以下のようになります。

【訂正】プロの力を身につける iPhone/iPadアプリケーション開発の教科書

「プロの力を身につける iPhone/iPadアプリケーション開発の教科書」の中に誤りがありましたので訂正させていただきます。

3-3 データを活用したアプリの作り方

235ページ下から4行目

訂正前

次のように、UILabel と UITextField
オブジェクトを配置します。Object Libraryから「Label」と「TextField」を選んで、ストーリーボードの「Detail」と表示されているシーンの上に配置してください。

訂正後

次のように、UIScrollView、UILabel 、UITextField オブジェクトを配置します。Object Libraryから「Scroll View」「Label」「Text Field」を選んで、ストーリーボードの「Detail」と表示されているシーンの上に配置してください。

UIScrollViewについての記述が抜けていました。

237ページ最終行

訂正前

次のように、UILabel と UITextField オブジェクトを配置してください

訂正後

次のように、UIScrollView、UILabel 、UITextField オブジェクトを配置してください。

235ページと同じく、UIScrollViewについての記述が抜けていました。

239ページソースコード

訂正前

@synthesize detailItem = _detailItem;

- (Person *)detailItem
{
  if (!_detailItem) {
    _detailItem = [NSEntityDescription
                          insertNewObjectForEntityForName:@"person")
                                         inManagedObjectContext:self.managedObjectContext];
    _detailItem.address = [NSEntityDescription
                                       insertNewObjectForEntityForName:@"address")
                                                      inManagedObjectContext:self.managedObjectContext];
  }
  return _detailItem;
}

訂正後

@synthesize detailItem = _detailItem;

- (Person *)detailItem
{
  if (!_detailItem) {
    _detailItem = [NSEntityDescription
                          insertNewObjectForEntityForName:@"Person")
                                         inManagedObjectContext:self.managedObjectContext];
    _detailItem.address = [NSEntityDescription
                                       insertNewObjectForEntityForName:@"Address")
                                                      inManagedObjectContext:self.managedObjectContext];
  }
  return _detailItem;
}

insertNewObjectForEntityForName: に渡す引数が小文字はじまりになっていました。正しくは大文字はじまりでした。

今後、間違いを見つけ次第この記事に追記していきます。

間違いがないように編集者さんと何度もレビューをしてチェックをしたつもりでしたがもれがありました。この場を借りで読者の皆様にお詫び申し上げます。また間違いを指摘してくださいました読者様ありがとうございました。
今後も本書の間違いや訂正があればこの記事に追記して行きます。