Google+ もご覧ください
ユーザーアイコン

iOSアプリ開発こぼれ話

iOS 6/7で游ゴシック体やヒラギノ丸ゴシックなどの追加ダウンロードフォントを使うには

K katsumi

iOS 6/7で游ゴシック体やヒラギノ丸ゴシックなどの追加ダウンロードフォントを使うには

追加ダウンロードフォント
追加ダウンロードフォント

OS XではMarvericks (10.9) から游ゴシック体游明朝体が標準搭載されたことで話題になりましたが、実はiOSでも6以上からこれらのフォントが使用できます。

iOS 6から追加ダウンロードフォントという仕組みが導入され、游ゴシック体などのフォントはその追加ダウンロードフォントという形で提供されています。アプリケーションから利用するには必要に応じてダウンロードします。

iOS 6で使えるフォントはAppleのiOS 6:フォントリストに記載されています。このページの下の方に追加情報として記載されているフォントがダウンロードフォントになります。

上記のページに記載されている追加ダウンロードフォントのうち、日本語のフォントは以下の8つです。

  • Hiragino Kaku Gothic Std W8 (ヒラギノ角ゴ Std W8)
  • Hiragino Kaku Gothic StdN W8 (ヒラギノ角ゴ StdN W8)
  • Hiragino Maru Gothic Pro W4 (ヒラギノ丸ゴ Pro W4)
  • Hiragino Maru Gothic ProN W4 (ヒラギノ丸ゴ ProN W4)
  • YuGothic Bold (游ゴシック体 ボールド)
  • YuGothic Medium (游ゴシック体 ミディアム)
  • YuMincho Demibold (游明朝体 デミボールド)
  • YuMincho Medium (游明朝体 ミディアム)

このうちヒラギノに関してはProNとPro、StdNとStdの違いで2書体になってるので実質6書体ですね。iOS 7ではさらに少しフォントが追加されているのですがそれについては別の記事で記載します。

追加ダウンロードフォントを利用するには、必要に応じてダウンロード処理をします。ダウンロードにはCoreText.frameworkの下記のAPIを利用します。

bool CTFontDescriptorMatchFontDescriptorsWithProgressHandler(
    CFArrayRef                          descriptors,
    CFSetRef                            mandatoryAttributes,
    CTFontDescriptorProgressHandler     progressBlock) CT_AVAILABLE(10_9, 6_0);

このAPIはドキュメントに解説が載っていないので、利用方法はヘッダファイルを見ます。また、Appleから公式のサンプルコードが提供されていますのでこちらも参考になります。

DownloadFont - iOS Developer Library

情報は少ないですが、特に難しいことはありません。1番目の引数にFont Descriptorの形でフォント情報を渡すと、適宜ブロックのコールバックが呼ばれるという仕組みです。

追加ダウンロードフォントについては、フォント名がわかっているのでフォント名からFont Descriptorを作ります。

NSDictionary *attributes = @{(id)kCTFontNameAttribute: fontName};
CTFontDescriptorRef fontDescriptor = CTFontDescriptorCreateWithAttributes((__bridge CFDictionaryRef)attributes);
NSArray *fontDescriptors = @[(__bridge id)fontDescriptor];
CFRelease(fontDescriptor);

そしてFont Descriptorを先ほどのAPIに渡すと必要に応じてフォントをダウンロードし、アプリケーションで利用できるようにロードしてくれるところまで実行してくれます。ダウンロードの進捗やトータルのサイズといった情報はブロックの引数のprogressParameterに入っています。

ダウンロードされたフォントはアプリケーションごとに保存されるわけではなく、すべてのアプリケーションで共有の場所に保存されます。例えば下記の場所です。

file:///private/var/mobile/Library/Assets/com_apple_MobileAsset_Font/83f5ce0efa7a810b73a7231c0e107f2955f2c85c.asset/AssetData/Yu%20Gothic%20Bold.otf

つまり、あるアプリケーションでフォントがダウンロードされていれば、別のアプリケーションではダウンロード処理はスキップしてそのフォントを利用できます。ただ、一度ダウンロードされたフォントでもデバイスの空き容量によって削除されることがあるので、プログラムはそれを考慮して作成する必要があります。

また、すでに他のアプリケーションでフォントがダウンロードされていても自分のアプリケーションで利用可能にするには上記のAPIを呼ぶ必要があります。さらにどのアプリケーションでダウンロードしたかにかかわらず、一度アプリケーションを終了するとアンロードされてしまいますので次に起動したときには再度上記のAPIを呼ぶ必要があります。

まとめると、 - 追加ダウンロードフォントはデバイス全体で共有される。 - ダウンロードされたフォントは、自動的にシステムから削除されることがある。 - フォントがダウンロード済みならダウンロード処理は自動的にスキップされる。 - フォントが他のアプリケーションによってダウンロード済みであっても、自分のアプリケーションで利用可能にするにはダウンロードのAPIを呼ぶ必要がある。 - 自分のアプリケーションでダウンロードしたフォントであっても、アプリケーションを終了したらアンロードされるので、再度利用可能な状態にするにはダウンロードのAPIを呼ぶ必要がある。

ややこしいように感じますが、ダウンロードのAPIは必要ならダウンロードされ、ダウンロード済みならダウンロードはスキップされてロード処理だけをする、という動きをします。要するに最初に利用しようとしたときにダウンロードのAPIを呼べばいいということになります。 ただ、単純にダウンロードのAPIを毎回呼ぶとすると、ダウンロードしたくないときにもダウンロードされてしまうので、必要に応じてダウンロードはキャンセルできるようにしたほうがいいでしょう。

追加ダウンロードフォントを利用可能にする処理は下記のようになります。ダウンロード処理はフォントがどのアプリケーションによってもダウンロードされてないときのみ行われます。

フォントがダウンロード済みであっても、アプリケーションを起動しただけの状態では利用可能になっていないので、この処理をする必要があります。その場合、コールバックはダウンロードのステータスになることはなく、短時間でkCTFontDescriptorMatchingDidFinishの状態になります。

CTFontDescriptorMatchFontDescriptorsWithProgressHandler((__bridge CFArrayRef)fontDescriptors, NULL, ^bool(CTFontDescriptorMatchingState state, CFDictionaryRef progressParameter) {
    NSDictionary *parameter = (__bridge NSDictionary *)progressParameter;
    double progressValue = [parameter[(id)kCTFontDescriptorMatchingPercentage] doubleValue];

    if (state == kCTFontDescriptorMatchingDidBegin) { // 処理の開始に1度だけ呼ばれる
        dispatch_async( dispatch_get_main_queue(), ^ {
            // ダウンロードはサブスレッドで行われるのでUIの更新などはメインスレッドで行う
            ...
        });
    } else if (state == kCTFontDescriptorMatchingDidFinish) { // 処理の終了時に1度だけ呼ばれる
        dispatch_async( dispatch_get_main_queue(), ^ {
            UIFont *font = [UIFont fontWithName:fontName size:1.0f];
            // この時点でフォントが利用可能になる
            ...
        });
    } else if (state == kCTFontDescriptorMatchingWillBeginDownloading) {
        // フォントが未ダウンロードの場合のみ、ダウンロードの開始前に呼ばれる
        ...
    } else if (state == kCTFontDescriptorMatchingDownloading) {
        // ダウンロード中、ダウンロードの進捗によって適宜呼ばれる
        ...
    } else if (state == kCTFontDescriptorMatchingDidFinishDownloading) {
        // ダウンロード完了時に呼ばれる
        ...
    } else if (state == kCTFontDescriptorMatchingDidFailWithError) {
        // ダウンロードが失敗したときに呼ばれる
        ...
    }

    return (bool)YES;
});

ダウンロード済みのフォントがある場合にフォントのロードだけ行い、ダウンロードはしたくないという場合は例えば下記のようにします。

CTFontDescriptorMatchFontDescriptorsWithProgressHandler((__bridge CFArrayRef)fontDescriptors, NULL, ^bool(CTFontDescriptorMatchingState state, CFDictionaryRef progressParameter) {
    if (state == kCTFontDescriptorMatchingDidFinish) {
        dispatch_async( dispatch_get_main_queue(), ^ {
            UIFont *font = [UIFont fontWithName:fontName size:1.0f];
            if (font) {
                if ([self.delegate respondsToSelector:@selector(fontDownloaderDidFinish:fontName:)]) {
                    [self.delegate fontDownloaderDidFinish:self fontName:fontName];
                }
            }
        });
    } else if (state == kCTFontDescriptorMatchingWillBeginDownloading) {
        return (bool)NO;
    }

    return (bool)YES;
});

kCTFontDescriptorMatchingWillBeginDownloadingNOを返しているのでそれ以上の処理は行われません。もしダウンロード中のキャンセルをサポートする場合にも同じようにすることで実現できます。

Appleのサンプルコードは主に中国語フォントを使っているので、日本語フォントについて簡単に試せるようにしたコードをGithubで公開しているので、よかったらご覧ください。

Ios talk
タグ:

記事をリクエストする

関連記事

コメント