iOS8での「Hiragino Sans」の罠

こんにちは増島です。

iOS10のリリースがもうすぐですね!
エンジニアの方は嬉しさとともに10対応が心配なのではないでしょうか。

サポート対象にするOSはアプリごとに異なると思いますが、ある程度の歴史があったりで10リリース以降も暫くはiOS8もサポート対象にするプロジェクトも多いかと思います。

作成中のアプリでは10リリース以降もiOS8以降をターゲットにする方針なのですが、 タイトルの内容で少しハマってしまったのでお話したいと思います!

hiragino-sample

新規に画面を作成したところiOS8のみ遷移に時間が掛かってしまうという事象がありました。

今回はサンプルということで、この通り一つUILabelがあるだけの簡単な画面で事象を再現します。

こちらの画面の読み込みにかかる時間を計測してみます!

AppDelegate.swift

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {  
    // 起動時の時間を保持する
    startTime = NSDate()
    return true
}

ViewController.swift

var startTime: NSDate?

class ViewController: UIViewController {

  override func viewDidLoad() {
    super.viewDidLoad()
    // 経過時間を表示する
    let elapsed = NSDate().timeIntervalSinceDate(startTime!)
    print("ロード完了:\(elapsed)")
  }
}

アプリ起動時の時間を保持して、最初の画面のロード完了後に経過時間を表示しています。

このような単純な画面でもiOS9とiOS8では大きな差がありました。

iOS9.3の時

ロード完了:0.00331300497055054

iOS8.1の時

ロード完了:2.26986300945282

約2秒の差が出てしまっていますね!
この場合アプリのTop画面なのでそれほど違和感は感じないのですが、 途中の画面への遷移の場合はアプリが固まる感じになってしまいとても気になります。

なぜこのような事象を発生するのでしょうか!?

Interface Builderからフォントをヒラギノ角ゴシックとして指定しています。

Xode7以降のInterface Builderからヒラギノ角ゴシックを指定する場合はfamilyに「Hiragino Sans」を指定する必要があります。

「Hiragino Sans」はiOS8には存在しないfont-familyです。 この状態でiOS8でアプリを起動した場合は最初にフォントのダウンロード処理が実行され、その後に画面がロードされるようです。 そのため2秒ほども時間が掛かってしまったのです。

従来のように「ヒラギノ角ゴシック」を使用したい場合はInterface Builderではなくコードから指定することによってパフォーマンスの低下を防ぐことができます。

class ViewController: UIViewController {

  @IBOutlet weak var messageLabel: UILabel!

  override func viewDidLoad() {
    super.viewDidLoad()
    self.messageLabel.font = UIFont(name: "HiraKakuProN-W3", size: 17)
    let elapsed = NSDate().timeIntervalSinceDate(startTime!)
    print("ロード完了:\(elapsed)")
  }
}

iOS9.3の時

ロード完了:0.0129449963569641

iOS8.1の時

ロード完了:0.0196570158004761

ほぼ同一のパフォーマンスになりましたね!

今回はiOS8での問題でしたが、OSがアップデートされ続ける限りこのような問題は起こりえるでしょう。

画面ロードに時間がかかる場合はOS毎の対応フォントも疑ってみてはいかがでしょうか!