Backporting Collection View Compositional Layouts

Backporting Collection View Compositional Layouts

What is Collection View Compositional Layouts?
What problem does it solve?
Anatomy: Compositional Layouts
Backporting Collection View Compositional Layouts

- iPlayground 2019 -

9bf923e39671cde83584e3e926296c13?s=128

Kishikawa Katsumi

September 21, 2019
Tweet

Transcript

  1. Backporting Collection View Compositional Layouts Kishikawa Katsumi

  2. Agenda • What is Collection View Compositional Layouts? • What

    problem does it solve? • Anatomy: Compositional Layouts • Backporting Collection View Compositional Layouts
  3. What is Collection View Compositional Layouts?

  4. What is Collection View Compositional Layouts? • New API introduced

    by iOS 13 • Extremely easy to build custom complex collection view layouts • Powerful features to beat complex layouts for modern apps
  5. https://developer.apple.com/videos/play/wwdc2019/215/ WWDC 2019

  6. https://developer.apple.com/videos/play/wwdc2019/215/ WWDC 2019

  7. Example: App Store.app

  8. Modern App UI is Complicated! • Various rich contents
 must

    be displayed • Various sizes of content are
 on a scroll view • Nested scrolling
  9. How to make it? • Collection View or
 Stack View

    with Scroll View? • Collection View on Collection View • Collection View Custom Layouts
  10. Problem: Building Custom Collection View Layouts

  11. How to Build Custom Collection View Layouts • Subclass UICollectionViewLayout

    • Override methods as needed.
  12. The problem Why difficult to build custom collection view layouts

  13. Why Difficult to Build Custom Collection View Layouts? • Implement

    everything yourself • I don't know where to start • Error prone • Too flexible, overkill
  14. UICollectionViewLayout class UICollectionViewLayout { class var layoutAttributesClass: AnyClass class var

    invalidationContextClass: AnyClass func prepare() func layoutAttributesForElements(in rect: CGRect) func layoutAttributesForItem(at indexPath: IndexPath) func layoutAttributesForSupplementaryView(ofKind: String, at: IndexPath) func layoutAttributesForDecorationView(ofKind: String, at: IndexPath) func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) func invalidationContext(forBoundsChange newBounds: CGRect) func shouldInvalidateLayout(forPreferredLayoutAttributes:, withOriginalAttributes:) func invalidationContext(forPreferredLayoutAttributes:, withOriginalAttributes:) func targetContentOffset(forProposedContentOffset: CGPoint, withScrollingVelocity: CGPoint) func targetContentOffset(forProposedContentOffset: CGPoint) var collectionViewContentSize: CGSize ...
  15. Easy Simple Complex Collection View on Collection View Custom Layouts

    Hard Flow Layout Layout Layout
  16. Easy Simple Complex Collection View on Collection View Custom Layouts

    Hard Flow Layout Implementation Layout Layout
  17. Easy Simple Complex Collection View on Collection View Custom Layouts

    Hard Flow Layout Compositional Layouts Implementation Layout Layout
  18. Anatomy: Compositional Layouts

  19. App Store.app

  20. None
  21. Layout, Section, Group, Item Anatomy: Compositional Layouts

  22. Layout, Section, Group, Item Anatomy: Compositional Layouts

  23. Layout, Section, Group, Item Anatomy: Compositional Layouts

  24. Layout, Section, Group, Item Anatomy: Compositional Layouts

  25. Layout, Section, Group, Item Anatomy: Compositional Layouts

  26. Layout, Section, Group, Item Layout

  27. Layout, Section, Group, Item Layout Section

  28. Layout, Section, Group, Item Section Layout

  29. Layout, Section, Group, Item Section Layout

  30. Layout, Section, Group, Item Layout Section Group

  31. Layout, Section, Group, Item Layout Section Group

  32. Layout, Section, Group, Item Layout Section Group

  33. Layout, Section, Group, Item Layout Section Group

  34. Layout, Section, Group, Item Layout Section Group

  35. Layout, Section, Group, Item Layout Section Group

  36. Layout, Section, Group, Item Layout Section Group Item

  37. Layout, Section, Group, Item Layout Section Group Item

  38. Layout, Section, Group, Item Layout Section Group Item

  39. Layout, Section, Group, Item Layout Section Group Item

  40. Layout, Section, Group, Item Layout Section Group Item

  41. Layout, Section, Group, Item Layout Section Group Item

  42. Section

  43. Group

  44. Group Item

  45. let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .fractionalHeight(1)) let item =

    NSCollectionLayoutItem(layoutSize: itemSize) let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .absolute(50)) let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item]) let section = NSCollectionLayoutSection(group: group)
  46. let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .fractionalHeight(1)) let item =

    NSCollectionLayoutItem(layoutSize: itemSize) let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .absolute(50)) let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item]) let section = NSCollectionLayoutSection(group: group)
  47. let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .fractionalHeight(1)) let item =

    NSCollectionLayoutItem(layoutSize: itemSize) let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .absolute(50)) let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item]) let section = NSCollectionLayoutSection(group: group)
  48. let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .fractionalHeight(1)) let item =

    NSCollectionLayoutItem(layoutSize: itemSize) let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .absolute(50)) let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item]) let section = NSCollectionLayoutSection(group: group)
  49. let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .fractionalHeight(1)) let item =

    NSCollectionLayoutItem(layoutSize: itemSize) let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .absolute(50)) let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item]) let section = NSCollectionLayoutSection(group: group)
  50. let itemSize = ...Size(widthDimension: .fractionalWidth(1), heightDimension: .fractionalHeight(1)) 100% 100%

  51. let itemSize = ...Size(widthDimension: .fractionalWidth(0.5), heightDimension: .fractionalHeight(1)) 50% 100% 50%

    100%
  52. let groupSize = ...Size(widthDimension: .fractionalWidth(1), heightDimension: .absolute(50)) 100% 50pt

  53. let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .fractionalHeight(1)) let item =

    NSCollectionLayoutItem(layoutSize: itemSize) let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .absolute(50)) let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item]) let section = NSCollectionLayoutSection(group: group)
  54. None
  55. let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .fractionalHeight(1)) let item =

    NSCollectionLayoutItem(layoutSize: itemSize) let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(0.92), heightDimension: .fractionalHeight(0.4)) let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item]) let section = NSCollectionLayoutSection(group: group)
  56. let groupSize = ...Size(widthDimension: .fractionalWidth(0.92), heightDimension: .fractionalHeight(0.4)) 92% 40%

  57. None
  58. let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .fractionalHeight(1)) let item =

    NSCollectionLayoutItem(layoutSize: itemSize) let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(0.92), heightDimension: .fractionalHeight(0.3)) let group = NSCollectionLayoutGroup.vertical(layoutSize: groupSize, subitem: item, count: 3) let section = NSCollectionLayoutSection(group: group)
  59. let group = NSCollectionLayoutGroup.vertical(layoutSize: groupSize, subitem: item, count: 3)

  60. let layout = UICollectionViewCompositionalLayout { (sectionIndex, environment) -> NSCollectionLayoutSection? in

    switch sectionIndex { case 0: ... return section case 1: ... return section ... } } Compose Them into Layout
  61. None
  62. None
  63. None
  64. None
  65. None
  66. None
  67. None
  68. None
  69. None
  70. None
  71. ... let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(0.92), heightDimension: .fractionalHeight(0.4)) let group

    = NSCollectionLayoutGroup.vertical(layoutSize: groupSize, subitems: [itemGroup]) let section = NSCollectionLayoutSection(group: group) section.orthogonalScrollingBehavior = .groupPagingCentered
  72. Orthogonal Scrolling Behavior ... let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(0.92), heightDimension:

    .fractionalHeight(0.4)) let group = NSCollectionLayoutGroup.vertical(layoutSize: groupSize, subitems: [itemGroup]) let section = NSCollectionLayoutSection(group: group) section.orthogonalScrollingBehavior = .groupPagingCentered
  73. None
  74. None
  75. None
  76. None
  77. None
  78. https://github.com/kishikawakatsumi/AppStore-Clone-CollectionViewCompositionalLayouts

  79. Only iOS 13?

  80. https://github.com/kishikawakatsumi/IBPCollectionViewCompositionalLayout

  81. IBPCollectionViewCompositionalLayout

  82. Under development ✅ Spacing ✅ Nested Groups ✅ Supplemental Views

    (e.g. Section Header/Footers) ✅ Pinned Section Header/Footers ✅ Orthogonal Scrolling Behavior ✅ Estimated Size (Autosizing) ✅ Custom Group Item (Absolute Positions) ✅ Drop-in replacement ❌ RTL Support
  83. None
  84. Drop-in Replacement - (instancetype)initWithSection:(IBPNSCollectionLayoutSection *)section { if (@available(iOS 13, *))

    { return [[NSClassFromString(@"UICollectionViewCompositionalLayout") alloc] initWithSection:section]; } else { IBPUICollectionViewCompositionalLayoutConfiguration *configuration = [IBPUICollectionViewCompositionalLayoutConfiguration defaultConfiguration]; return [self initWithSection:section configuration:configuration]; } }
  85. None
  86. UICollectionViewLayout class UICollectionViewLayout { ... func prepare() func layoutAttributesForElements(in rect:

    CGRect) -> [UICollecti func layoutAttributesForItem(at indexPath: IndexPath) -> UIColle func layoutAttributesForSupplementaryView(ofKind elementKind: St func layoutAttributesForDecorationView(ofKind elementKind: Strin func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) - ... var collectionViewContentSize: CGSize { get } ...
  87. UICollectionViewLayout class UICollectionViewLayout { ... func prepare() func layoutAttributesForElements(in rect:

    CGRect) -> [UICollecti func layoutAttributesForItem(at indexPath: IndexPath) -> UIColle func layoutAttributesForSupplementaryView(ofKind elementKind: St func layoutAttributesForDecorationView(ofKind elementKind: Strin func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) - ... var collectionViewContentSize: CGSize { get } ...
  88. UICollectionViewLayout class UICollectionViewLayout { ... func prepare() func layoutAttributesForElements(in rect:

    CGRect) -> [UICollecti func layoutAttributesForItem(at indexPath: IndexPath) -> UIColle func layoutAttributesForSupplementaryView(ofKind elementKind: St func layoutAttributesForDecorationView(ofKind elementKind: Strin func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) - ... var collectionViewContentSize: CGSize { get } ...
  89. UICollectionViewLayout class UICollectionViewLayout { ... func prepare() func layoutAttributesForElements(in rect:

    CGRect) -> [UICollecti func layoutAttributesForItem(at indexPath: IndexPath) -> UIColle func layoutAttributesForSupplementaryView(ofKind elementKind: St func layoutAttributesForDecorationView(ofKind elementKind: Strin func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) - ... var collectionViewContentSize: CGSize { get } ...
  90. None
  91. None
  92. Wrap up • Collection View Compositional Layouts is incredible! •

    No need to implement collection view layout subclass • Like just editing configurations • Keep your code simple regardless layout become complex • You can use it now! (without iOS 13)
  93. Pitfalls

  94. No Documentation

  95. 2019-09-20 07:14:13.535020+0900 ... [29591:20271214] [CompositionalLayout] Attempting to add contentInsets to

    an item's dimension along an estimated axis. This layout axis insets will be ignored (however, any non-estimated axis insets will be applied). Please consider using the item's edgeSpacing or the containing group's interItemSpacing instead.
  96. References • IBPCollectionViewCompositionalLayout
 https://github.com/kishikawakatsumi/IBPCollectionViewCompositionalLayout • App Store.app Clone
 https://github.com/kishikawakatsumi/AppStore-Clone-CollectionViewCompositionalLayouts

  97. References • Advances in Collection View Layout
 https://developer.apple.com/videos/play/wwdc2019/215/ • All

    you need to know about UICollectionViewCompositionalLayout
 https://medium.com/flawless-app-stories/all-what-you-need-to-know-about- uicollectionviewcompositionallayout-f3b2f590bdbe • Using CollectionView Compositional Layouts in Swift 5
 https://dev.to/kevinmaarek/using-collectionview-compositional-layouts-in-swift-5-1nan • Move your cells left to right, up and down on iOS 13 — Part 1
 https://medium.com/shopback-engineering/move-your-cells-left-to-right-up-and-down-on- ios-13-part-1-1a5e010f48f9