iOSアプリで動的にフォントサイズを変更する「Dynamic Type」新機能の実装法 #ios7yahoo

iOS 7で追加された機能と実装について紹介していく本シリーズ。今回紹介するのは画面に表示される文字サイズを自由に調整できる「Dynamic Type」です。
この機能を実装する手法について、「Yahoo!知恵袋」のアプリ開発担当兼プロジェクトマネージャー小川航佑さんに寄稿いただきました。

動的にフォントサイズを変更する「Dynamic Type」機能

iOS 7で追加された新機能の中で、Appleがとくに強調しているのが「Dynamic Type」です。ユーザーはこの機能を使うことにより、画面に表示される文字サイズを自由に調整できるようになりました。文字サイズを小さくしても大きくしても、Dynamic Typeによってテキストは常に読みやすく、レイアウトは崩れることはありません。

ただし、この機能はアプリ側がDynamic Type機能をサポートしている必要があります。とは言っても、対応はさほど難しいわけではなく、これまでサイズや書体等を細かく指定していた箇所を、「Body」や「Title」のような指定に書き換えるだけです。これだけであとは、OSが自動的に読みやすい適切な書体を割り当ててくれます。また、行間などのレイアウト調整も動的に行われるため、開発者の負担はとても軽くなります。

8,568通り、あなたはどのタイプ?

メールアプリやメモアプリが採用

iOSの標準アプリでは、メールアプリやメモアプリがDynamic Type機能をサポートしています。

実際に文字サイズを変更して、どのように表示が変わるか試してみると良いでしょう。

8,568通り、あなたはどのタイプ?

実装方法

では、実装方法を説明していきます。ここでは実装例として、Dynamic Type機能をサポートしたテーブルビューを作っていきます。

まずは、いろいろな長さのテキストを各セルに表示するだけのテーブルビューを作成します。

次に、このテキストをDynamic Typeに対応させます。具体的には、フォントサイズを数値で設定している箇所を、 テキストスタイル で設定するようにします。コードで設定する方法と、Storyboardから設定する方法の二通りが存在します。

コードで設定する方法

これまで

UIFont *font = [UIFont systemFontOfSize:[UIFont systemFontSize]];

のようにフォントサイズを指定していたところを

[UIFont preferredFontForTextStyle:UIFontTextStyleBody];

のように スタイル で指定します。こうすることで、適切な書体をOS側で動的に割り当ててくれます。スタイルには次の値を指定できます。

  • UIFontTextStyleHeadline
  • UIFontTextStyleBody
  • UIFontTextStyleSubheadline
  • UIFontTextStyleFootnote
  • UIFontTextStyleCaption1
  • UIFontTextStyleCaption2

各スタイルがどのように表示されるか、いろいろ試してみると面白いです。

Storyboardから設定する場合

Storyboardを開き、セルの上に載っているUILableをクリックします。画面右側のユーティリティエリアからAttributes Inspector(右から3番目のアイコン)を開き、「Font」に BodyHeadline などのText Styleを指定します。

これでテキストをDynamic Typeに対応させることができました。

試しに文字サイズ設定を最大にしてアプリを起動してみましょう。

先ほどより文字サイズが大きく表示されていると思います。

セルの高さを動的に調整する

このままだと文字サイズが大きい時に、テキストが少ししか表示されずに省略されてしまいます。これを文字サイズに合わせてセルの高さを調整するように修正してみます。

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // セルに表示される文字列
    NSString *labelText = [self.textArray objectAtIndex:indexPath.row];

    // セルに表示される文字列のフォント
    UIFont *labelFont = [UIFont preferredFontForTextStyle:UIFontTextStyleBody];

    // 余白
    CGFloat PADDING_OUTER = 30.0;

    CGRect totalRect =
    [labelText boundingRectWithSize:CGSizeMake(self.tableView.frame.size.width, CGFLOAT_MAX)
                            options:(NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading)
                         attributes:[NSDictionary dictionaryWithObject:labelFont forKey:NSFontAttributeName]
                            context:nil];
    return totalRect.size.height + PADDING_OUTER;
}

上記のように、テキストからサイズを計算し、

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath;

メソッドでセルの高さを調整しています。

テキストのサイズは、

- (CGRect)boundingRectWithSize:(CGSize)size options:(NSStringDrawingOptions)options attributes:(NSDictionary *)attributes context:(NSStringDrawingContext *)context;

メソッドを使って計算しています。

iOS 6までよく使われていた sizeWithFont 系のメソッドは、iOS 7からはまとめて Deprecated になったようです。

これでテキストに合わせて、セルの高さが調整されるようになりました。

文字サイズの変更を監視する

この状態では、iPhoneの設定から文字サイズが変更された場合でも、アプリを再起動するまでは文字サイズが反映されません。

これを解消するために、 UIContentSizeCategoryDidChangeNotification を監視して、文字サイズが変更された際に通知を受け取れるようにします。

[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(preferredContentSizeChanged:)
                                             name:UIContentSizeCategoryDidChangeNotification
                                           object:nil];

通知を受け取ったらテーブルビュー全体をリロードして、変更後の文字サイズを反映させます。

- (void)preferredContentSizeChanged:(NSNotification *)aNotification
{
    [self.tableView reloadData];
}

これで文字サイズの設定変更を即座に反映できるようになりました。

参考資料

本記事についてのスライド、サンプルコードは以下をご覧ください。

  • サンプルコード | GitHub

その他にもYahoo! JAPAN Tech BlogではiOS 7勉強会に関する様々な資料を公開しています。ぜひご覧ください。

寄稿者プロフィール 小川 航佑

ヤフー株式会社に勤務。2010年からiOSアプリ開発に従事。様々なアプリ開発を経て、現在はQ&A共有アプリ「Yahoo!知恵袋」のアプリ開発担当兼プロジェクトマネージャー。AB型。
Twitter: @koogawa / ブログ: http://koogawa.hateblo.jp/

※本記事は「CodeIQ MAGAZINE」掲載の記事を転載しております。

PC_goodpoint_banner2

Pagetop