CGLayout-iOSの自動レイアウトの新しいシステム

こんにちはHabr!

最新のオープンソース開発CGLayout



制限に基づいた、Autolayoutに続くiOSの2番目のマークアップシステムを紹介したいと思います。













「別の自動レイアウトシステム...なぜ?何のため?」 -あなたはおそらく考えた。

確かに、iOSコミュニティは既にかなりの数のレイアウトライブラリを作成していますが、Autolayoutは言うまでもなく、それらのどれも手動レイアウトの真に大規模な代替手段になりませんでした。







CGLayout



は抽象エンティティと連携します。これにより、UIView、CALayerを同時に使用して、 not rendered



オブジェクトでnot rendered



マークアップを構築not rendered



。 また、単一の座標空間があるため、階層の異なるレベルにある要素間に依存関係を構築できます。 バックグラウンドスレッドでの作業が可能で、キャッシュが容易で、拡張が容易で、さらに多くの機能があります。







CGLayout



大規模なプロジェクトに発展する見込みのCGLayout



機能的な製品です。







しかし、当初は、いつものように、あなたの人生を単純化するという目標が当たり前でした。

Autolayoutのパフォーマンスが低いため、または複雑なロジックのために、誰もが手動レイアウトを作成する必要がある場合があります。 したがって、いくつかの拡張機能は常に書かれていました(la setFrameThatFitsなど)。







このような拡張機能を実装するプロセスでは、より複雑な順序のアイデアが発生しますが、通常のように時間の不足により、これらはすべてTrelloレコードのフレームワーク内に残り、永久に停止します。

しかし、ようやく実現に至り、地平線が見えなくなったとき、あなたは中毒になり、停止することはすでに非現実的です。 それでも、時間が無駄にならなかったことを願っています。フレームワークの機能がなければ、サンプルコードがあれば、私のソリューションが誰かの生活を楽にしてくれることを願っています。







私の決定の話に加えて、他のフレームワークを分析して比較しようとするので、それは退屈ではないと思います。







比較



機能性



必要条件 フレックスレイアウト ASDK (テクスチャ) レイアウトキット 自動レイアウト Cglayout
性能 + + + - +
キャッシュ可能性 + + + +- +
マルチスレッド - + + - +
階層間レイアウト - - - + +
CALayerおよび「レンダリングなし」ビューのサポート - + - - +
拡張性 - + + - +
テスタビリティ + + + + +
宣言的 + + + + +


いくつかの指標は主観的かもしれません、なぜなら 実稼働環境ではこれらのフレームワークを使用しませんでした。 間違えた場合は、修正してください。







性能



テストには、 LayoutFrameworkBenchmarkが使用されました。













AsyncDisplayKitは、ベンチマーク開発者によって含まれていなかったため、チャートに追加されませんでした。また、ASDKは、バックグラウンドでレイアウトを実行しますが、パフォーマンス測定では完全に公平ではありません。 または、Pinterestアプリを見ることができます。 そこのパフォーマンスは本当に印象的です。







分析



すでに多くのフレームワークに関する多くの情報があります、私は私の意見を共有するだけです。 主にネガティブな側面についてお話します。なぜなら、それらはすぐに明らかになり、プロはフレームワークのページで説明されているからです。







レイアウトキット



Github 25の問題







非常に柔軟ではありませんが、大量のコードを作成し、実装にプログラマを十分に含める必要があります。 LinkedIn以外のアプリケーションでLayoutKitを使用することに関する情報は見つかりませんでした。

それでも、LayoutKitが目標を達成しなかったという意見があり、LinkedInアプリケーションのフィードは依然として遅くなります。







機能:









FlexLayout(別名YogaKit)



Github 65の問題

アイト







レイアウトのみを処理する機能を提供します。 他のグッズもチップもありません。

機能:









AsyncDisplayKit(テクスチャ)



Github 200の問題

アイト







Facebookは、より高いレベルのスレッドセーフな抽象化を作成しました。 UIKitツールスタック全体の実装が必要になった理由。 重いライブラリです。使用することにした場合、後で拒否することはできません。 それでも、これは依然として最も有能で開発されたオープンソースソリューションです。

機能:









Cglayout





現在の制限:









まだ実装されていないもの:







  1. RTLサポート。
  2. 階層からビューを削除するときの動作。
  3. macOS、tvOSをサポートします。
  4. 特性コレクションのサポート。
  5. 再利用可能なビューをマークするための便利なデザインはありません。
  6. 現在のレイアウト構成を動的に変更します。


CGLayoutの実装



CGLayout



、Swift言語の現代の原則に基づいて構築されています。

CGLayoutでのマークアップ管理の実装は、 RectBasedConstraint



RectBasedLayout



RectBasedConstraint



3つの基本プロトコルに基づいていLayoutItem









規約

LayoutItem



Iを実装するすべてのエンティティはレイアウト要素を呼び出し、他のすべてのエンティティは単なるレイアウトエンティティです。







凡例






基本的なマークアップ



 public protocol RectBasedLayout { func layout(rect: inout CGRect, in source: CGRect) }
      
      





RectBasedLayout



レイアウトを変更するための動作を宣言し、このための1つのメソッドを定義します。利用可能なスペースを基準にした方向付けが可能です。







RectBasedLayout



プロトコルを実装するLayout



構造は、レイアウト要素の完全かつ十分なレイアウトを決定します。 位置決めと寸法。

したがって、 Layout



は2つの要素に分割されます:配置Layout.Alignment



と塗りつぶしLayout.Filling



です。 これらは、水平および垂直レイアウトで構成されています。 すべての構成要素はRectBasedLayout



実装しRectBasedLayout



。 これにより、さまざまなレベルの複雑さのレイアウト要素を使用してマークアップを実装できます。 すべてのエンティティレイアウトは、実装によって簡単に拡張できます。







レイアウト構造の複合図:






制限事項



すべての制限はRectBasedConstraint



によって実装されRectBasedConstraint



RectBasedLayout



エンティティが利用可能なスペースでマークアップを定義する場合、 RectBasedConstraint



エンティティRectBasedConstraint



この利用可能なスペースをRectBasedConstraint



します。







 public protocol RectBasedConstraint { func constrain(sourceRect: inout CGRect, by rect: CGRect) }
      
      





LayoutAnchor



は、環境から抽象化された動作を持つ特定の区切り文字(サイド、サイズなど)が含まれます。

現時点では、主なリミッターが実装されています。







LayoutAnchor構造の複合図:






レイアウトの制約



 public protocol LayoutConstraintProtocol: RectBasedConstraint { var isIndependent: Bool { get } func layoutItem(is object: AnyObject) -> Bool func constrainRect(for currentSpace: CGRect, in coordinateSpace: LayoutItem) -> CGRect }
      
      





レイアウト要素またはコンテンツ(テキスト、画像など)への依存を決定します。 これらは、制約のソースと使用される制約に関するすべての情報を含む自己完結型の制約です。

LayoutConstraint



特定の区切り文字セットを持つレイアウト要素に関連付けられた制約。

AdjustLayoutConstraint



レイアウト要素に関連付けられた制約には、サイズベースの制約が含まれます。 AdjustableLayoutItem



プロトコルをサポートするレイアウト要素で使用できます。







レイアウト要素



 public protocol LayoutItem: class, LayoutCoordinateSpace { var frame: CGRect { get set } var bounds: CGRect { get set } weak var superItem: LayoutItem? { get } }
      
      





UIView、CALayerなどのクラスによって実装され、 not rendered



れてnot rendered



クラスも実装さnot rendered



ます。 スタックビューなどの他のクラスを実装することもできます。







フレームワークにはLayoutGuideの実装があります。 これはUIKitのUILayoutGuideに似ていますが、要素をファクトリ化する機能があります。 これにより、LayoutGuideをプレースホルダーとして使用できます。これは、最新の設計ソリューションに照らして非常に重要です。 特に、ViewPlaceholderクラスはこれらの目的のために作成されています。 UIViewControllerと同じビュー読み込みパターンを実装します。 したがって、彼と一緒に仕事をすることは非常に馴染みのあるものです。







サイズを計算できる要素の場合、プロトコルが宣言されます:







 public protocol AdjustableLayoutItem: LayoutItem { func sizeThatFits(_ size: CGSize) -> CGSize }
      
      





デフォルトでは、UIViewのみが実装します。







レイアウト座標空間



 public protocol LayoutCoordinateSpace { func convert(point: CGPoint, to item: LayoutItem) -> CGPoint func convert(point: CGPoint, from item: LayoutItem) -> CGPoint func convert(rect: CGRect, to item: LayoutItem) -> CGRect func convert(rect: CGRect, from item: LayoutItem) -> CGRect var bounds: CGRect { get } var frame: CGRect { get } }
      
      





レイアウトシステムには、 LayoutCoordinateSpace



プロトコルの形式で表示される統合座標システムがあります。







各タイプの基本実装(UIView、CALayer、UICoordinateSpace +相互変換のための独自の実装)を使用しながら、すべてのレイアウト要素に対して単一のインターフェイスを作成します。







レイアウトブロック



 public protocol LayoutBlockProtocol { var currentSnapshot: LayoutSnapshotProtocol { get } func layout() func snapshot(for sourceRect: CGRect) -> LayoutSnapshotProtocol func apply(snapshot: LayoutSnapshotProtocol) }
      
      





レイアウトブロックは、レイアウトの完全かつ独立したユニットです。 マークアップを実行し、スナップショットを受信/適用するためのメソッドを定義します。







LayoutBlock



は、レイアウト要素、そのメインレイアウト、およびLayoutConstraintProtocolを実装する制約をカプセル化します。







マークアップを更新するプロセスは、制限を使用して使用可能なスペースを決定することから始まります。 システムはまだ競合制限の問題を解決せず、どのような方法でも優先順位を付けないため、制限の適用に慎重に取り組む必要があることに留意してください。 したがって、一般的に、サイズベースの制約(AdjustLayoutConstraint)は、位置ベースの制約の後に配置する必要があります。 スーパービューのスペース(境界)がソーススペースとして使用されます。 各制限により、使用可能なスペースが変更されます(トリム、シフト、ストレッチなど)。 制限が機能すると、結果のスペースがLayout



に転送され、そこで要素の実際のマークアップが計算されます。







LayoutScheme



は、他のレイアウトブロックを組み合わせて、マーキングの正しいシーケンスを決定するブロックです。







レイアウトのスナップショット



 public protocol LayoutSnapshotProtocol { var snapshotFrame: CGRect { get } var childSnapshots: [LayoutSnapshotProtocol] { get } }
      
      





LayoutSnapshot



一連のフレームとして表示されるスナップショットで、レイアウト要素の階層を保持します。







延長



すべての拡張可能な要素は、拡張プロトコルを実装します。







 public protocol Extended { associatedtype Conformed static func build(_ base: Conformed) -> Self }
      
      





したがって、機能を拡張するときに、 CGLayout



既に定義されている型を使用して、厳密に型指定されたインターフェイスを構築できます。







使用例



CGLayoutは、一連の更新フレームを構築するという意味で、手動レイアウトとほとんど変わりません。 CGLayoutでマークアップを実装する場合、プログラマーは、作業を開始する前にすべての制限が実際のフレームに適用されなければならないことを覚えておく必要があります。







 let leftLimit = LayoutAnchor.Left.limit(on: .outer) let topLimit = LayoutAnchor.Top.limit(on: .inner) let heightEqual = LayoutAnchor.Size.height() ... let layoutScheme = LayoutScheme(blocks: [ distanceLabel.layoutBlock(with: Layout(x: .center(), y: .bottom(50), width: .fixed(70), height: .fixed(30))), separator1Layer.layoutBlock(with: Layout(alignment: separator1Align, filling: separatorSize), constraints: [distanceLabel.layoutConstraint(for: [leftLimit, topLimit, heightEqual])]) ... ]) ... override public func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() layoutScheme.layout() }
      
      





この例では、このラベルの位置が決定された後、セパレーターはdistanceLabel



制約を使用します。







まとめ



自動レイアウトは、安定性、優れたAPI、強力なサポートにより、依然としてレイアウトのメインツールです。 しかし、サードパーティのソリューションは、焦点が狭いか柔軟性があるため、特定の問題の解決に役立ちます。







CGLayout



は、レイアウトプロセスを記述するための通常のロジックがまったくないため、ある程度慣れる必要があります。

まだ多くの作業がありますが、これは時間の問題であり、このようなシステムの分野でニッチを占めることができる多くの利点があることはすでに明らかです。 フレームワークの動作はまだ実稼働環境でテストされていないため、試してみる機会があります。 フレームワークはテストでカバーされているため、大きな問題はありません。







フレームワークのさらなる開発に積極的に参加していただければ幸いです。







Githubリポジトリ







そして最後に、Habr-usersに質問したいと思います。

レイアウトシステムの要件は何ですか?

レイアウトの構築で最も嫌いなことは何ですか?








All Articles