【iOS】UIFontをアプリケーションで一括設定しつつ、個別でフォントを分ける方法

2014.03.05

こんにちは!

先日、ヤフー VS クラスメソッドでiBeaconの運用について発表を担当させて頂きました、荒川です。

協力して下さった大橋さんや諏訪さんをはじめとしたクラスメソッドのメンバー、また観客の方々にも恵まれて無事インターン生対決を勝利する事が出来ました。

いいね!を押して下さった方、本当にありがとうございました!

話しは変わって、以前はゲームアプリを作る事が多く、UIKit関連やStoryboardはほとんど触っていませんでしたが、最近やっと慣れてきました。

それで、アプリで使うフォントを一括で設定したくなり、UILabelのappearanceで設定しようと思ったらハマったのでまとめます。

iOS 7で利用頻度の増えたappearance

ご存知の通り、iOS 7からステータスバーやナビゲーションバーの背景がデフォルトで透過になり、フラットデザイン化の流行にも合わせてtintColor(各UIに指定できる色合い)等をカスタマイズするアプリが増えています。

例えば、「Twitter」や「Facebook」といったアプリは画面上部ステータスバー+ナビゲーションバーやタブバーの色を統一することにより全体的にiOS 7っぽさを出しています。

一般的にこれらは画面全てに適用することが多いため、appDelegateでステータスバーの背景を画像に変える、

[UINavigationBar appearance].barTintColor = [UIColor colorWithPatternImage:(UIImage *)];

やタブバーの選択色を変える、

[UITabBar appearance].tintColor = [UIColor colorWithRed:(CGFloat) green:(CGFloat) blue:(CGFloat) alpha:(CGFloat)];

といった処理を追加して、アプリ全体で統一している事が多いです。

この方法を使って、アプリ内で使われるUILabelのフォントを統一しようと思いました。

どのようにしてハマったのかを検証用プロジェクトで説明します。

UILabelのapperance一括設定検証

uifont_01_mini

早速検証用のプロジェクトを作り、Storyboard上にLabelを2つ貼付けます。(1つは比較用です。) ついでに対応するクラスにはプロパティを結びつけました。

この時点で使っているArial RegularフォントはStoryboardで設定しました。

アプリを制作していくとフォントを統一した方が見栄えが良かったので、appDelegateで以下のように統一させました。

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    // Override point for customization after application launch.

    // 一括でラベルのフォントをDidotのサイズ18.0fに統一する
    [[UILabel appearance] setFont:[UIFont fontWithName:@"Didot" size:18.0f]];

    return YES;
}

これを設定後、再度起動してみると

uifont_02_mini

うまくUILabelのフォントが統一されました。 しかしStoryboard上では、あたかも自分で決めたフォントが個別に適用されているかのように見えています。

そして注意しなければならないのは - (void)viewDidLoadメソッドのタイミングではStoryboardのフォントが適用され、実際に画面に出力される- (void)viewDidAppear:(BOOL)animatedのタイミングではappearanceで統一されたフォントが適用されていることです。 - (void)viewDidLoadのタイミングでフォント情報にアクセスしにいくとデフォルトだとSystemフォント、Storyboardで指定していればStoryboardで指定したフォントが返ってきます。

しかし実際に表示されているのはappearanceで統一したDidotのサイズ18なので、表示されているフォント情報とは違う情報が取れてしまいます。

今回は最初にStoryboardでフォントを指定しましたが、元々System(デフォルト)フォントのままのLabelを使っていると、

「この上の文字だけ太字にできない?強調して見せたいんだよね!だからもう少し大きくもできないかな?」

と、いう意見があったとして、Storyboard上でBoldやSizeの変更を個別に加えて、いざ実行してみると統一されたフォントになってしまいます。

uifont_03_mini

実行してみると

uifont_04_mini

このように強制的にappearanceのフォントにされてしまいます。

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.

    NSLog(@"%s", __func__);

    self.lblText1.font = [UIFont fontWithName:@"Arial Bold" size:24.0f];

    NSLog(@"Arial Label = %@", _lblText1.font.fontDescriptor);
    NSLog(@"System Label = %@", _lblText2.font.fontDescriptor);
}

直そうと思って上記のように- (void)viewDidLoadメソッドでフォントを個別に設定してみると

uifont_05_mini

Helvetica Neueになってしまいました。 なんだかログもめちゃくちゃだし・・・。

調べてみたら、Storyboardでは「Arial Bold」と表示されていますが、実際は「Arial-BoldMT」とするみたいです。 ちなみにこのBoldMTの指定はフォントによって-Heavyだったりただの-Boldだったり・・・どうやら表現がフォントによって色々とあるようです。

これでハマっていて、

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.

    NSLog(@"%s", __func__);

    self.lblText1.font = [UIFont fontWithName:@"Arial-BoldMT" size:24.0f];

    NSLog(@"Arial Label = %@", _lblText1.font.fontDescriptor);
    NSLog(@"System Label = %@", _lblText2.font.fontDescriptor);
}

仕方なく個別に設定しました。

すると

uifont_06_mini

期待した通りの表示にできました。

直前でセットしているので当然のことですが、画面に出力される- (void)viewDidAppear:(BOOL)animatedでのフォントの値も期待した値を取得できています。

結論

appDelegateなどでappearanceに設定すると、一括でUILabelのフォントを指定することが可能です。 しかし、Storyboardで配置したUILabelのフォントを個別に設定しても無視されてしまうので気をつけて下さい。

特に

「フォントはそのままでいいからここだけBoldにして。」

「ここの文字だけサイズを大きく(小さく)できない?」

といった事はよくあることだと思います。 こういった時に

- (void)viewDidLoad

メソッドで、LabelのUIFontプロパティをログ出力すると、「なんでログはBoldって書いてあるのにその通りに表示されないの???」とハマるかもしれません。私は

- (void)viewDidAppear:(BOOL)animated

でログを確認するまで気づきませんでした。

appearanceで一括設定している方に、少しでも参考になると幸いです。