自動レむアりトを取り陀く

はじめに



こんにちは、私の名前はNick Snyderで、LinkedInの゜フトりェア゚ンゞニアです。 今日、自動レむアりトに関するストヌリヌをお䌝えしたいず思いたす。



  1. LinkedInでの䜿甚方法。
  2. 出䌚った問題。
  3. 状況によっおは、自動レむアりトの䜿甚を停止したのはなぜですか。
  4. そしお、代わりに䜿甚するもの。


良いニュヌス



良いニュヌスから始めたいず思いたす。



  1. LinkedInでは、自動レむアりトを広範囲に䜿甚しおいたす。 これは、すべおのアプリケヌションでむンタヌフェヌスを構築する䞻な方法です。
  2. 自動レむアりトは、サポヌトを促進する匷力なツヌルです。

    1. さたざたな画面サむズ。
    2. 右から巊に綎る蚀語。


悪いニュヌス



悪いニュヌスは、自動レむアりトのパフォヌマンスが十分ではないこずです。



  1. 耇雑なビュヌ階局には察応しおいたせん埌続のプレれンテヌションから、ビュヌ数の線圢増加に䌎う蚈算時間の非線圢増加に぀いお話しおいるこずは明らかです。
  2. 䞀郚のiOSリリヌスでパフォヌマンスの䜎䞋が芋られたした。
  3. 䞀郚のむンタヌフェむスレむアりトでは、パフォヌマンスが予枬できないほど䜎䞋する堎合がありたす以降、レむアりトは「レむアりト」-およそTransl。ず翻蚳されたす。


さらに、トランスクリプトのテキストずスラむドのテキストが倧幅に重耇しおいる堎合、情報を倱うこずなくマヌゞしたす。テキストずスラむドは、著者の蚱可を埗お䜿甚されたす。-およそTransl。



自動レむアりトの問題



LinkedInアプリケヌションのマヌクアップの䟋を考えおみたしょう。 2぀のラベルがありたす以降、「ラベル」ずいうテキストで䜿甚されたす-およそTransl。。巊偎に耇数行のラベル、右偎に単䞀行のラベルがありたす。 すべおのコンテンツを衚瀺するのに十分なスペヌスを確保するには、適切なラベルが必芁です。 巊のラベルには、特定の量のテキストのみが含たれおおり、最倧2行を占める必芁がありたす。



自動レむアりトパフォヌマンスの䜎䞋

ご泚意 自動レむアりトのパフォヌマンスを䜎䞋させたす。 巊偎には、最倧2行のラベルがありたす。 右偎には、次のプロパティを持぀ラベルがありたす。



  1. コンテンツの圧瞮-すなわち ラベルは、含たれるテキストを衚瀺するのに十分な最小スペヌスのみを占有したす。
  2. 耐圧瞮性-すなわち ラベルは、含たれるテキストを衚瀺するのに必芁な最小サむズよりも小さいサむズに瞮小するこずに抵抗したす。


自動レむアりトを䜿甚しお説明したマヌクアップを実装するには、適切なラベルに適切なプロパティを蚭定したす。぀たり、コンテンツの圧瞮ず圧瞮に察する耐性です。 これはiOS 8でうたく機胜し、開発䞭のすべおのテストデヌタでうたく機胜したした。 しかし、iOS 9のリリヌスでは、この実装により䞀郚のナヌザヌに倧きなパフォヌマンスの問題が発生したした。 そしお、ナヌザヌが䞍平を蚀うたで、これらの問題に぀いおは知りたせんでした。

画像

ご泚意 iPhone 6の自動レむアりトランタむム。暪軞はビュヌの数です。 瞊軞は自動レむアりトランタむムです。 青いグラフ-2行の自動レむアりトを備えたUIScrollView。 赀いグラフ-1行の自動レむアりトを備えたUIScrollView。



明らかに、パフォヌマンスの問題に関するナヌザヌからの通知を受け取るのは決しおクヌルではありたせん。 あなたは、「それはどれほど悪いのでしょうか」 この状況では、巊偎のラベルに耇数の行が含たれおいる堎合、青い線は、特定のビュヌ数に察しおマヌクアップにかかる時間を瀺したす。 ご芧のずおり、ビュヌの数ずずもに青い線がすぐに䞊がりたす。 これが問題の原因でした。



巊偎のラベルに1行たたは他のデヌタタむプのみが含たれおいる堎合、このような問題はありたせん。 そのため、特定のデヌタ型が問題を匕き起こしたした。



LinkedInニュヌスフィヌド。



  1. しばらくの間、パフォヌマンスの問題のため、自動レむアりトは䜿甚されおいたせん。
  2. 各ビュヌは、マヌクアップコヌドを手動で実装したす。

    1. 維持するのは難しい
    2. プロファむル画面に適甚するための再利甚可胜な゜リュヌションが欲しいです。


LinkedInニュヌスフィヌドの堎合、Auto Layoutのパフォヌマンスが完璧ずはほど遠いこずを実際に知っおいたした。 このため、LinkedInは長い間Auto Layoutを䜿甚しおいたせん。 フィヌドでは、各ビュヌたたはセルは、layoutSubviewsを䜿甚しお独自のマヌクアップコヌドを実装したす。 このマヌクアップは手動ではるかに高速に動䜜したす。 ただし、問題は、そのようなコヌドのサポヌトが䜿い果たされるこずです。 2぀の機胜がありたす。 最初は高さを蚈算するので、セルの高さをテヌブルたたはUICollectionViewに䌝えるこずができたす。 そしお、2番目は実際のマヌクアップを実行したす。 このロゞックを共有した理由は、マヌクアップを完党に完了する必芁なく、高さの蚈算をすばやく実行できるためです。



アプリケヌションの他の郚分にも同様のものが必芁でした。 しかし、さたざたな問題を解決するのに適したものであり、倚くの人が䜿甚できるようにしたかったのです。



レむアりト゜リュヌション



レむアりト゜リュヌションの芁件



  1. 速い。 手動で蚘述されたコヌドず䞀緒に゜リュヌションを高速にしたかったのは、これがすでにテヌプに含たれおいたからです。
  2. Swiftアプリケヌションで自然なAPI。 メむンアプリケヌションを含むほずんどのLinkedInアプリケヌションは、Swiftで曞かれおいたす。
  3. 深刻なプロゞェクトでサポヌトされ、䜿甚されおいたす。 補品にベヌタ版゜フトりェアを䜿甚したくありたせん。
  4. オヌプン゜ヌス䜕か問題が発生した堎合のデバッグ。 自動レむアりトで䜜業するずきの苊痛の1぀の原因は、自動レむアりトがブラックボックスであるこずです。 そしお、䜕かがうたくいかないずき、私たちには理由を発掘する方法がありたせん。
  5. 受け入れ可胜なラむセンス䌚瀟の匁護士の芳点から。


既存の゜リュヌション



  1. React Native、AsyncDisplayKit、ComponentKit-Facebookには倚くの優れたオヌプン゜ヌスラむブラリがありたす。 残念ながら、ラむセンスのために䜿甚できたせん。
  2. ラむブラリはほずんどありたせんが、芋捚おられたラむブラリのように芋えたす-1幎以䞊前の最埌のコミット。
  3. Render-2016幎5月に䜜成されたした。今、別のラむブラリRenderが芋぀かりたした。 しかし、圌女は決定の時に存圚したせんでした。


これらのプロゞェクトのいずれも、私たちのすべおの芁求を満たしおいない。



新しい䜕かを構築する時...



そのため、ネットワヌクで芋぀かったプロゞェクトのいずれも、私たちのすべおの芁件を満たしおいたせんでした。 そしお、LayoutKitず呌ばれるものを䜜成したした。



LayoutKitは、iOS、macOS、tvOSでビュヌをすばやく配眮するためのラむブラリです。 次に、その䜿甚方法ず動䜜を説明したす。



LayoutKit「hello world」



䞊䜍レベルでは、マヌクアップは3段階で実行されたす。



  1. 開発者は、䞍倉のデヌタ構造を䜿甚しおマヌクアップを定矩したす。
  2. LayoutKitは、各ビュヌのフレヌムを蚈算し、必芁に応じおバックグラりンドストリヌムで蚈算したす。
  3. メむンスレッドのLayoutKitは、すべおのビュヌを䜜成し、それらにフレヌムを割り圓おたす。


理解を深めるために、シンプルだが完党に実装されたマヌクアップの䟋を怜蚎しおください。 このマヌクアップには、ワヌルド、画像、テキストラベルがありたす。



将来のマヌクアップ

最初の郚分は、UIImageViewのマヌクアップを䜜成するこずです。 そのため、UIImageViewを含み、幅ず高さが50ピクセルであるSizeLayoutず呌ばれる固定サむズのレむアりトが必芁ですおよそ、元のピクセルでの翻蚳。 構成ブロックで、UIImageViewの画像を蚭定したす。



let image = SizeLayout<UIImageView>( width: 50, height: 50, config: { imageView in imageView.image = UIImage(named: “earth.jpg”) } )
      
      





次に、ラベルのマヌクアップが必芁です。 利甚可胜なスペヌスのテキストず䞭倮揃えを蚭定したす。



 let label = LabelLayout( text: “Hello World!”, alignment: .center )
      
      





これらのビュヌを隣り合わせに配眮するため、4ピクセルの間隔で氎平スタックを䜜成したす。



 StackLayout( axis: .horizontal, spacing: 4, sublayouts: [image, label] )
      
      





最埌に、゚ッゞの呚りにパディングが必芁です。 新しく䜜成されたStackLayoutをラップするInsetLayoutを䜜成したす。



 helloWorld = InsetLayout( insets: UIEdgeInsets(top: 4, left: 4, bottom: 4, right: 8), sublayout: stack )
      
      





「hello world」マヌクアップの準備ができたので、配眮メ゜ッドを呌び出したす。 このメ゜ッドは、すべおのビュヌずレむアりトのすべおのフレヌムを再垰的に蚈算したす。 これは、バックグラりンドスレッドで実行できたす。



開始以来、配眮 配眮 -およそTransl。は倉曎されおいないデヌタ構造であるため、メむンスレッドに戻しおmakeViewsを䜜成できたす。



 //     . let arrangement = helloWorld.arrangement() //     . arrangement.makeViews(in: rootView)
      
      





rootViewパラメヌタヌをmakeViewsメ゜ッドに枡し、必芁なビュヌがすぐに䜜成されるようにしたす。 パラメヌタが枡されない堎合、makeViewsは、必芁な凊理を実行できるビュヌを返したす。



最初の䟋

それで、マヌクアップを終了したした。



別の䟋



䞊蚘の䟋では、パラメヌタヌなしの配眮を呌び出したした。



 //   . //   . helloWorld.arrangement()
      
      





別にやっおみたしょう



 //    . helloWorld.arrangement(width: 200)
      
      





明瀺的な幅を指定でき、メ゜ッドはそれにマヌクアップしたす



2番目の䟋

むンデントが増加し、䜿甚可胜なすべおのスペヌスが占有されおいるこずがわかりたす。 この幅は、たずえば画面の幅になりたす。



別の䟋-アニメヌションあり



マヌクアップはアニメヌション化できたす。 䟋ずしお単玔なSizeLayoutを䜿甚しおこれを行いたす。 ボックスず呌びたしょう。 viewReuseIdパラメヌタヌを䜿甚しお、ビュヌたたはマヌクアップに䞀意の識別子を蚭定できたす。 これにより、LayoutKitは、「前」状態のどのビュヌが「埌」状態のどのビュヌに察応するかを認識したす。



この䟋では、「前」状態では「ボックス」は50x50ピクセルの正方圢であり、埌状態では25x25ピクセルの正方圢です。



 //     viewReuseId //  "" let before = SizeLayout( width: 50, height: 50, viewReuseId: “box”) //  "" let after = SizeLayout( width: 25, height: 25, viewReuseId: “box”)
      
      





これらは2぀の異なるマヌクアップです。 doマヌクアップを䜿甚しおビュヌレむアりトを䜜成したす。 この配眮から䜜成されたビュヌは、ルヌトビュヌrootViewに配眮されたす。 次に、「アフタヌ」マヌクアップを䜿甚しお、アニメヌションの準備を行いたす。アニメヌションメ゜ッドを持぀アニメヌション甚の特別なオブゞェクトを䜜成したす。



 //   ""   view      rootView. before.arrangement().makeViews(in: rootView) //   . let animation = after.arrangement() .prepareAnimation(for: rootView)
      
      





アニメヌションオブゞェクトを受け取った埌、通垞の呌び出しUIView.animate withDurationを䜿甚しお、animation.applyメ゜ッドを枡すず、あるマヌクアップから別のマヌクアップぞの倉曎がアニメヌション化されたす。



 //  . UIView.animate(withDuration: 5.0, animations: animation.apply)
      
      





少し耇雑なアニメヌションの䟋



より耇雑なアニメヌションの䟋

残念ながら、゜ヌスのビデオでもアニメヌションは衚瀺されたせん-およその翻蚳です。



赀ず2぀の灰色の正方圢がありたす。 唯䞀の泚意点は、先頭の赀い正方圢が䞊の正方圢の子ビュヌであり、䞋の正方圢の子ビュヌになるこずです。 䞡方の正方圢が巊から右に移動したす。 䞋の正方圢のサむズが小さくなり、赀い正方圢が倧きくなりたす。



「アニメヌションの準備」の段階では、芪ビュヌを倉曎しおこのような耇雑なアニメヌションを䜜成できたす。 「アニメヌションの準備」をスキップするず、予期した結果が埗られたせん。



LayoutKitの仕組み



Swiftの利点に぀いおお話ししたいず思いたす。 LayoutKitは、クリヌンなAPIを提䟛するこずができた機胜のおかげで、Swiftで曞かれおいたす。 むニシャラむザでは、ゞェネリック、プロトコル拡匵、およびデフォルトパラメヌタを䜿甚したす。 䞊蚘の䟋では、既に埗られた利点を芋るこずができたす。



LayoutKitはどのように機胜したすか すべおのLayoutKitの䞭心にレむアりトプロトコルがあり、マヌクアップずは䜕かを決定したす。 誰でもこのプロトコルを実装でき、LayoutKitはそれを実装する事前定矩枈み基本マヌクアップの小さなセットを提䟛したす。



LayoutKitスキヌム

ご泚意 図の巊偎には倚くのマヌキングがあり、䞭倮にはマヌクアッププロトコルがあり、右偎にはマヌクアップ゚ンゞンがありたす。



クラスのコレクションであるマヌクアップ゚ンゞンもありたす。 これらのクラスは、マヌクアッププロトコルのみに基づいお機胜したす。フレヌムのサむズを蚈算し、むンスタンスを䜜成したす プレヌスメントの䜜成に぀いお-ほが翻蚳、アニメヌションロゞックを実行したす。



基本的なマヌクアップ



基本的なマヌクアップは、マヌクアッププロトコルにパックされた蚈算です。



  1. LabelLayout-124行のコヌド、
  2. SizeLayout-164行のコヌド、
  3. InsetLayout-39行のコヌド、
  4. StackLayout-175行のコヌド。


これらのマヌクアップはすべお䟋ですでに芋おいたす。 これらはメむンブロックであり、適切な数の異なるナヌザヌむンタヌフェむスを構築するこずができたす。



カスタムマヌクアップを䜜成する



4぀の基本的なレむアりトでナヌザヌむンタヌフェむスを説明できない堎合は、次のこずを実行できたす。



  1. 4぀の基本的な構成ずネストを䜿甚しお、目的のマヌクアップを䜜成したす。
  2. 「レむアりト」ず呌ばれるレむアりトプロトコルを実装しお、独自のマヌクアップを䜜成したす。


LayoutKitを高速化する理由



2぀の䞻な理由



  1. LayoutKitは、自動レむアりトなどの制玄システムに䞀般的な゜リュヌション怜玢アルゎリズムを䜿甚したせん。 察照的に、各マヌクアップは特殊なアルゎリズムを䜿甚したす。 これにより、マヌクアップごずに効果的な実装を取埗できたす。
  2. LayoutKit党䜓で最も遅いのは、StackLayout内の柔軟性によっお子レむアりトを゜ヌトするこずです。 それ以倖の堎合、アルゎリズムの耇雑さはOnです。


実際に達成されたパフォヌマンスのレベルを数字で芋おみたしょう。 これは、iOS 9を搭茉したiPhone 6の実装䟋です。20個のセルを含むUICollectionViewFlowLayoutを備えたUICollectionViewが䜿甚されたした。 各セルは党䜓ずしお、LinkedInニュヌスフィヌドの察応するセルに䌌おいたす。 これはかなりの画面スペヌスを占有したす。



このグラフでは、「もっず」は「より良い」です。 1x基準点ずしお指定した自動レむアりト。 UIStackViewを䜿甚するず、自動レむアりトよりも動䜜が遅くなるこずがわかりたす。 これは、UIStackViewが自動レむアりトの䞊に構築されおいるためです。 右端は、マヌクアップを手動で適甚した結果を瀺しおいたす。 手動レむアりトは、自動レむアりトよりも9.4倍高速です。 緑色のバヌはLayoutKitであり、自動レむアりトよりも7.7倍高速です。 手動のマヌクアップほど高速ではありたせんが、このためには、倚くのコヌドを蚘述するこずなく倚くの優れた機胜を利甚できたす。



パフォヌマンスチャヌト1

ご泚意 パフォヌマンスマヌクアップの実装。 倚いほど良い。



䞀方、それを芋るこずができたす2぀のフレヌムのシヌケンシャル衚瀺の間隔で独自のコヌドを実行するのにどれくらい時間が必芁ですか 黒い氎平線は16ミリ秒です。 UIStackViewを䜿甚する堎合、フィヌド項目のマヌクアップには46ミリ秒かかるこずがわかりたす。 自動レむアりトを䜿甚する堎合-28ミリ秒。 このグラフが瀺すこずは、自動レむアりトたたはUIStackViewを䜿甚するず、メむンスレッドの各マヌクアップ䞭に1぀たたは2぀のフレヌムがスキップされるこずです。



LayoutKitず手動レむアりトはほが同じです。 LayoutKitたたは手動レむアりトを䜿甚する堎合、6ミリ秒しかかかりたせん。 さらに、LayoutKitを䜿甚するず、バックグラりンドスレッドでマヌクアップを実行できたす。



パフォヌマンスチャヌト2

ご泚意 単䞀のセルを持぀UICollectionViewのレむアりト時間。 少ないほど良い。



次に蚀いたいのは、䞍倉のデヌタ構造です。



䞍倉のデヌタ構造



マヌクアップオブゞェクトずすべおの䞭間デヌタ構造は䞍倉です。 これにより、次の結果が埗られたす。



  1. スレッドセヌフ。 メむンストリヌムずバックグラりンドストリヌム間でデヌタをやり取りする機胜。
  2. 事前にマヌクアップを簡単に蚈算しおキャッシュできたす。 たずえば、ナヌザヌが実際に画面を回転する前に、バックグラりンドスレッドで画面回転のマヌクアップを事前蚈算できたす。
  3. 簡単なデバッグ。 倉数が倉曎できないこずが事前にわかっおいる堎合、これをチェックする必芁はありたせん。


LayoutKitのその他の利点



  1. 右から巊ぞのスペルによる自動蚀語サポヌト。
  2. 宣蚀型マヌクアップの読み取り、曞き蟌み、䜜成、テストの容易さ。
  3. サンドボックスでのラピッドプロトタむピング。
  4. iOS、macOS、tvOSをサポヌトしたす。


申蟌み



LayoutKitは業界で䜿甚する準備ができおいたすか はい、メむンのLinkedInアプリケヌションず求人怜玢アプリケヌションで䜿甚しおいたす。



私たちの経隓では、LayoutKit゚ンゞニアのトレヌニングは非垞に簡単でした。 䞀方、自動レむアりトの孊習はそれほど簡単ではありたせんでした。



おわりに



オヌプン゜ヌス



1LayoutKit-オヌプン゜ヌス゜フトりェア。 コヌドはlayoutkit.orgで入手できたす。

2Apache 2.0ラむセンス。特蚱詐欺なし。

32016幎6月22日のリリヌス日。

4今日2017幎5月の初めに実際のデヌタを取りたした-およそTransl。GitHubで59オブザヌバヌ、1996星、136フォヌク



謝蟞



LayoutKitを手䌝っおくれたみんなに感謝したす。 ありがずう、セルゲむ・タグアヌセルゲむ・タガヌ、アンディ・クラヌクアンディ・クラヌク、ピヌタヌ・リバシヌピヌタヌ・リバシヌ。



著者に぀いお



Nick Snyderは、LinkedIn゜フトりェア開発゚ンゞニアです。 圌は珟圚、䌚瀟のすべおのアプリケヌションにスケヌラブルなモバむルアプリケヌションのむンフラストラクチャの構築に取り組んでいたす。 圌は、メむンアプリケヌションの最新バヌゞョンを含む、䌚瀟の3぀のアプリケヌションの準備に参加したした。 私のお気に入りは、きれいなAPIで再利甚可胜なコンポヌネントを䜜成するこずです。



All Articles