『Akiba.swift × エウレカ』で「AutoLayout以外の選択肢」について発表しました

エウレカ iOS エンジニアのmuukiiです🤠

2018/3/28にエウレカで開催した『Akiba.swift』にて、iOSアプリ開発におけるレイアウトの話をしました。

AutoLayoutによってUIの動作が遅くなっている可能性があり、どのように高速化ができるのか。

という内容です。

スライドを作る前のメモをベースに記事にしておきます 🤩

もし「UIの動きが遅くて困ってる!」みたいなのがありましたら私でよければ相談乗ります 🙋🏻‍♂️

AutoLayout以外の選択肢

コードで書くAutoLayout

NSLayoutConstraintとiOS9から使えるNSLayoutAnchorを使用する。

NSLayoutAnchorの登場によりコードでも相当書きやすくなりましたが、それでもまだ惜しい感じです。

そのため、より簡潔に記述するためのライブラリがたくさん存在しています。

これだけの数が存在していることから、IBではなくコードでレイアウトを記述したいニーズがあることが考えられます

AutoLayoutはパフォーマンスの問題を抱えている

AutoLayoutは制約の数が増えるとどんどん遅くなる

引用 : https://github.com/layoutBox/FlexLayout#performanceんです。

リフレッシュレートが60Hzのディスプレイにおける一回のイベントループでブロックして良い時間は、約16msです。
この間にUIKit自体の処理も入るので私たちが書くコードは10ms程度に抑えられると良さそうです。

余談ですが、最近の iPad Proは120Hzになってるので、もっと速くしないとディスプレイの価値は発揮されないです。

一方で、動きの少ない一枚の画面であれば大きな問題にはなりません。

例えば、ログイン画面のようなシンプルな画面です。
むしろスクロール可能でない画面におけるマルチデバイス対応のためのレスポンシブレイアウトではAutoLayoutはかなり強力です。

また、AutoLayoutの制約を工夫して高速化を行う記事がありました。

UITableView/UICollectionViewで60fpsが欲しい

fpsの向上にはCellのサイズ計算よりもCellの中身を設定するタイミングが重要です。

Cellのサイズ計算はreloadData直後に全て行われる

  • contentSizeを確定するため
  • しかし、estimated~を使用している場合、動作は異なります。 (ここはあんまり詳しくないです😱 そして、いい思い出がない。)

Viewに変更が走ると再計算が走る

  • 制約が存在するUIViewにaddSubview
  • 制約の組まれたUILabel.textを変更する時など
  • textが変わるとUILabelから`invalidateIntrinsictContentSize`が呼ばれ再レイアウト

AutoLayoutを使わない方法を考える

全部は紹介しきれないのでいくつかピックアップします。

🧘🏻‍♀ ️facebook/Yoga

  • AutoLayoutと比較して8倍程度高速らしい
    https://github.com/layoutBox/FlexLayout#performance
  • PinLayoutのほうがさらに速いらしいけど今回は割愛
    これ高速かつAutoLayoutみたいなこと出来るのでオススメかも
  • CSS Flexbox Layoutと非常に近い動作と記述方式
    Flexboxの基本はUIStackViewのverticalとhorizontalを組み合わせてレイアウトする感覚に近い
  • コアとなるコードはC++で3000行ほど
  • ReactNativeでも使われている
    これはつまり、ReactNativeのアプリの方がUIの動作が速い可能性があるってことです。

FlexLayout(Yoga)を使ったCellの高さ計算のサンプルはここにあります。

TextureGroup/Texture

もとの名はAsyncDisplayKit

  • Pinterestアプリで全面的に使用されている
  • Pinterestを触るとその強さが分かる
  • Objective-C++で実装されている

ライブラリ利用者はUIKitコンポーネントを操作するのではなくNodeというオブジェクトを通して操作する

  • Node -> UIViewへの展開はTextureが行う (AsyncにDisplayする)
  • MainThreadがUI操作で忙しければ展開を見送る仕組みがある
  • レイアウトはNode内にコードで記述

驚くほどの最適化

  • メインスレッドはUIのインタラクション用に空けておく
  • レスポンス速度が最大化

UILabelやUIImageViewは使われずにレイヤーにレンダリング

  • タップ判定の必要のないものはCALayerとして表示 (layerBacking)
  • UIViewよりメモリ消費が抑えられるため

バックグラウンドでメモリ解放を行うキューがある

deallocationも一定のコストがかかるため多くのNodeオブジェクトがメインスレッドで解放されてしまうとUIが固まる可能性があります。

これを防ぐためにバックグラウンドスレッド上で良いタイミングでdeallocを行うキュー(runloop)が動いています。

Nodeに記述したレイアウトはバックグラウンドでサイズ計算が可能

  • concurrentで実行される
  • Nodeが持つデータが他スレッドに依存していなければ、CPUのコアがフルで使える
  • iPhoneXなら5個Cell同時に計算 😲

現時点で最も最適化されたフロントエンドライブラリ
=> 直列でも高速な処理を並行で処理可能にしているから

まとめ(感想)

AutoLayoutは柔軟で強力なレイアウトエンジンだけど、まだパフォーマンス面で課題があると思います。

現状、CPUのクロック数の伸びには期待しづらいので、UIパフォーマンス向上のためにはマルチスレッドによるアプローチが必須になってくるはずです。
しかし、UIKitは現時点ではマルチスレッドには対応していないんですよね。

こういった現状からFacebook, Instagram, PinterestのようなサービスはプロダクトではAutoLayoutの使用を避けており、それぞれがUIパフォーマンスを最大化するOSSを公開しています。

- Pinterest — Texture
- Facebook — ComponentKit, ReactNative, Texture
- Instagram — IGListKit (ちょっと趣旨は違うけど)

このような状況に対して、今後Appleが取るUIKitへの考えが楽しみです。
AutoLayout頑張って欲しいですね!


Eureka Engineering

Learn about Eureka’s engineering efforts, product developments and more.

Muukii (Hiroshi Kimura)

Written by

iOS Engineer & Head of Development, Pairs Global at Eureka, Inc. https://twitter.com/muukii_app

Eureka Engineering

Learn about Eureka’s engineering efforts, product developments and more.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade