Unverified Commit eb2d32c5 authored by Apptek Studios's avatar Apptek Studios Committed by GitHub
Browse files

Merge pull request #36 from apptekstudios/dev

Greatly improved layout syntax
Custom Delegates can access data per item
parents 0d17edf5 0d827255
Showing with 623 additions and 669 deletions
+623 -669
......@@ -4,6 +4,11 @@
<dict>
<key>SchemeUserState</key>
<dict>
<key>ASCollectionViewDemo-ReleaseConfig.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>1</integer>
</dict>
<key>ASCollectionViewDemo.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
......
......@@ -10,110 +10,6 @@ struct AppStoreScreen: View
(Lorem.title, DataSource.appsForSection($0))
}
var layout: ASCollectionViewLayout<Int>
{
ASCollectionViewLayout<Int>(scrollDirection: .vertical, interSectionSpacing: 20)
{ sectionID -> ASCollectionViewLayoutSection in
switch sectionID
{
case 0:
return ASCollectionViewLayoutCustomCompositionalSection
{ (environment, _) -> NSCollectionLayoutSection in
let columnsToFit = floor(environment.container.effectiveContentSize.width / 320)
let item = NSCollectionLayoutItem(layoutSize: NSCollectionLayoutSize(
widthDimension: .fractionalWidth(1.0),
heightDimension: .fractionalHeight(1.0)))
let itemsGroup = NSCollectionLayoutGroup.horizontal(
layoutSize: NSCollectionLayoutSize(
widthDimension: .fractionalWidth(0.9 / columnsToFit),
heightDimension: .absolute(280)),
subitems: [item])
itemsGroup.contentInsets = NSDirectionalEdgeInsets(top: 10, leading: 8, bottom: 10, trailing: 8)
let section = NSCollectionLayoutSection(group: itemsGroup)
section.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 20, bottom: 0, trailing: 20)
section.orthogonalScrollingBehavior = .groupPaging
return section
}
case 1:
return ASCollectionViewLayoutCustomCompositionalSection
{ (environment, _) -> NSCollectionLayoutSection in
let columnsToFit = floor(environment.container.effectiveContentSize.width / 320)
let item = NSCollectionLayoutItem(layoutSize: NSCollectionLayoutSize(
widthDimension: .fractionalWidth(1.0),
heightDimension: .fractionalHeight(1.0)))
let itemsGroup = NSCollectionLayoutGroup.vertical(
layoutSize: NSCollectionLayoutSize(
widthDimension: .fractionalWidth(1.0),
heightDimension: .fractionalHeight(1.0)),
subitem: item, count: 2)
itemsGroup.interItemSpacing = .fixed(10)
let nestedGroup = NSCollectionLayoutGroup.horizontal(
layoutSize: NSCollectionLayoutSize(
widthDimension: .fractionalWidth(0.9 / columnsToFit),
heightDimension: .absolute(180)),
subitems: [itemsGroup])
nestedGroup.contentInsets = NSDirectionalEdgeInsets(top: 10, leading: 8, bottom: 10, trailing: 8)
let header = NSCollectionLayoutBoundarySupplementaryItem(
layoutSize: NSCollectionLayoutSize(
widthDimension: .fractionalWidth(1.0),
heightDimension: .estimated(34)),
elementKind: UICollectionView.elementKindSectionHeader,
alignment: .top)
header.contentInsets.leading = nestedGroup.contentInsets.leading
header.contentInsets.trailing = nestedGroup.contentInsets.trailing
let section = NSCollectionLayoutSection(group: nestedGroup)
section.boundarySupplementaryItems = [header]
section.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 20, bottom: 0, trailing: 20)
section.orthogonalScrollingBehavior = .groupPaging
return section
}
default:
return ASCollectionViewLayoutCustomCompositionalSection
{ (environment, _) -> NSCollectionLayoutSection in
let columnsToFit = floor(environment.container.effectiveContentSize.width / 320)
let item = NSCollectionLayoutItem(layoutSize: NSCollectionLayoutSize(
widthDimension: .fractionalWidth(1.0),
heightDimension: .fractionalHeight(1.0)))
let itemsGroup = NSCollectionLayoutGroup.vertical(
layoutSize: NSCollectionLayoutSize(
widthDimension: .fractionalWidth(1.0),
heightDimension: .fractionalHeight(1.0)),
subitem: item, count: 3)
itemsGroup.interItemSpacing = .fixed(10)
let nestedGroup = NSCollectionLayoutGroup.horizontal(
layoutSize: NSCollectionLayoutSize(
widthDimension: .fractionalWidth(0.9 / columnsToFit),
heightDimension: .absolute(240)),
subitems: [itemsGroup])
nestedGroup.contentInsets = NSDirectionalEdgeInsets(top: 10, leading: 8, bottom: 10, trailing: 8)
let header = NSCollectionLayoutBoundarySupplementaryItem(
layoutSize: NSCollectionLayoutSize(
widthDimension: .fractionalWidth(1.0),
heightDimension: .estimated(34)),
elementKind: UICollectionView.elementKindSectionHeader,
alignment: .top)
header.contentInsets.leading = nestedGroup.contentInsets.leading
header.contentInsets.trailing = nestedGroup.contentInsets.trailing
let section = NSCollectionLayoutSection(group: nestedGroup)
section.boundarySupplementaryItems = [header]
section.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 20, bottom: 0, trailing: 20)
section.orthogonalScrollingBehavior = .groupPaging
return section
}
}
}
}
func header(withTitle title: String) -> some View
{
HStack
......@@ -162,7 +58,7 @@ struct AppStoreScreen: View
var body: some View
{
ASCollectionView(sections: self.sections)
.layoutCompositional(self.layout)
.layout(self.layout)
.edgesIgnoringSafeArea(.all)
.navigationBarTitle("Apps", displayMode: .inline)
}
......@@ -213,7 +109,114 @@ struct AppStoreScreen: View
}
}
struct OrthoView_Previews: PreviewProvider
extension AppStoreScreen
{
var layout: ASCollectionLayout<Int>
{
ASCollectionLayout(scrollDirection: .vertical, interSectionSpacing: 20)
{ sectionID in
switch sectionID
{
case 0:
return ASCollectionLayoutSection
{ environment in
let columnsToFit = floor(environment.container.effectiveContentSize.width / 320)
let item = NSCollectionLayoutItem(layoutSize: NSCollectionLayoutSize(
widthDimension: .fractionalWidth(1.0),
heightDimension: .fractionalHeight(1.0)))
let itemsGroup = NSCollectionLayoutGroup.horizontal(
layoutSize: NSCollectionLayoutSize(
widthDimension: .fractionalWidth(0.9 / columnsToFit),
heightDimension: .absolute(280)),
subitems: [item])
itemsGroup.contentInsets = NSDirectionalEdgeInsets(top: 10, leading: 8, bottom: 10, trailing: 8)
let section = NSCollectionLayoutSection(group: itemsGroup)
section.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 20, bottom: 0, trailing: 20)
section.orthogonalScrollingBehavior = .groupPaging
return section
}
case 1:
return ASCollectionLayoutSection
{ environment in
let columnsToFit = floor(environment.container.effectiveContentSize.width / 320)
let item = NSCollectionLayoutItem(layoutSize: NSCollectionLayoutSize(
widthDimension: .fractionalWidth(1.0),
heightDimension: .fractionalHeight(1.0)))
let itemsGroup = NSCollectionLayoutGroup.vertical(
layoutSize: NSCollectionLayoutSize(
widthDimension: .fractionalWidth(1.0),
heightDimension: .fractionalHeight(1.0)),
subitem: item, count: 2)
itemsGroup.interItemSpacing = .fixed(10)
let nestedGroup = NSCollectionLayoutGroup.horizontal(
layoutSize: NSCollectionLayoutSize(
widthDimension: .fractionalWidth(0.9 / columnsToFit),
heightDimension: .absolute(180)),
subitems: [itemsGroup])
nestedGroup.contentInsets = NSDirectionalEdgeInsets(top: 10, leading: 8, bottom: 10, trailing: 8)
let header = NSCollectionLayoutBoundarySupplementaryItem(
layoutSize: NSCollectionLayoutSize(
widthDimension: .fractionalWidth(1.0),
heightDimension: .estimated(34)),
elementKind: UICollectionView.elementKindSectionHeader,
alignment: .top)
header.contentInsets.leading = nestedGroup.contentInsets.leading
header.contentInsets.trailing = nestedGroup.contentInsets.trailing
let section = NSCollectionLayoutSection(group: nestedGroup)
section.boundarySupplementaryItems = [header]
section.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 20, bottom: 0, trailing: 20)
section.orthogonalScrollingBehavior = .groupPaging
return section
}
default:
return ASCollectionLayoutSection
{ environment in
let columnsToFit = floor(environment.container.effectiveContentSize.width / 320)
let item = NSCollectionLayoutItem(layoutSize: NSCollectionLayoutSize(
widthDimension: .fractionalWidth(1.0),
heightDimension: .fractionalHeight(1.0)))
let itemsGroup = NSCollectionLayoutGroup.vertical(
layoutSize: NSCollectionLayoutSize(
widthDimension: .fractionalWidth(1.0),
heightDimension: .fractionalHeight(1.0)),
subitem: item, count: 3)
itemsGroup.interItemSpacing = .fixed(10)
let nestedGroup = NSCollectionLayoutGroup.horizontal(
layoutSize: NSCollectionLayoutSize(
widthDimension: .fractionalWidth(0.9 / columnsToFit),
heightDimension: .absolute(240)),
subitems: [itemsGroup])
nestedGroup.contentInsets = NSDirectionalEdgeInsets(top: 10, leading: 8, bottom: 10, trailing: 8)
let header = NSCollectionLayoutBoundarySupplementaryItem(
layoutSize: NSCollectionLayoutSize(
widthDimension: .fractionalWidth(1.0),
heightDimension: .estimated(34)),
elementKind: UICollectionView.elementKindSectionHeader,
alignment: .top)
header.contentInsets.leading = nestedGroup.contentInsets.leading
header.contentInsets.trailing = nestedGroup.contentInsets.trailing
let section = NSCollectionLayoutSection(group: nestedGroup)
section.boundarySupplementaryItems = [header]
section.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 20, bottom: 0, trailing: 20)
section.orthogonalScrollingBehavior = .groupPaging
return section
}
}
}
}
}
struct AppStoreScreen_Previews: PreviewProvider
{
static var previews: some View
{
......
......@@ -24,9 +24,9 @@ struct InstaFeedScreen: View
{ item, _ in
StoryView(post: item)
})
.layoutCompositional(scrollDirection: .horizontal)
.layout(scrollDirection: .horizontal)
{
ASCollectionViewLayoutList(itemSize: .absolute(100), sectionInsets: NSDirectionalEdgeInsets(top: 0, leading: 10, bottom: 0, trailing: 10))
.list(itemSize: .absolute(100), sectionInsets: NSDirectionalEdgeInsets(top: 0, leading: 10, bottom: 0, trailing: 10))
}
.frame(height: 100)
.scrollIndicatorsEnabled(false)
......
......@@ -42,7 +42,7 @@ struct MagazineLayoutScreen: View
var body: some View
{
ASCollectionView(sections: self.sections)
.layoutCustom { MagazineLayout() }
.layout { MagazineLayout() }
.customDelegate(ASCollectionViewMagazineLayoutDelegate.init)
.edgesIgnoringSafeArea(.all)
.navigationBarTitle("Magazine Layout (custom delegate)", displayMode: .inline)
......
......@@ -17,43 +17,39 @@ struct PhotoGridScreen: View
typealias SectionID = Int
var layout: ASCollectionViewLayout<Int>
var layout = ASCollectionLayout<Int>(scrollDirection: .vertical, interSectionSpacing: 0)
{
ASCollectionViewLayout(
scrollDirection: .vertical,
interSectionSpacing: 0)
{
ASCollectionViewLayoutCustomCompositionalSection(sectionLayout: { (layoutEnvironment, _) -> NSCollectionLayoutSection in
let isWide = layoutEnvironment.container.effectiveContentSize.width > 500
let gridBlockSize = layoutEnvironment.container.effectiveContentSize.width / (isWide ? 5 : 3)
let gridItemInsets = NSDirectionalEdgeInsets(top: 5, leading: 5, bottom: 5, trailing: 5)
let itemSize = NSCollectionLayoutSize(widthDimension: .absolute(gridBlockSize), heightDimension: .absolute(gridBlockSize))
let item = NSCollectionLayoutItem(layoutSize: itemSize)
item.contentInsets = gridItemInsets
let verticalGroupSize = NSCollectionLayoutSize(widthDimension: .absolute(gridBlockSize), heightDimension: .absolute(gridBlockSize * 2))
let verticalGroup = NSCollectionLayoutGroup.vertical(layoutSize: verticalGroupSize, subitem: item, count: 2)
let featureItemSize = NSCollectionLayoutSize(widthDimension: .absolute(gridBlockSize * 2), heightDimension: .absolute(gridBlockSize * 2))
let featureItem = NSCollectionLayoutItem(layoutSize: featureItemSize)
featureItem.contentInsets = gridItemInsets
let fullWidthItemSize = NSCollectionLayoutSize(widthDimension: .absolute(layoutEnvironment.container.effectiveContentSize.width), heightDimension: .absolute(gridBlockSize * 2))
let fullWidthItem = NSCollectionLayoutItem(layoutSize: fullWidthItemSize)
fullWidthItem.contentInsets = gridItemInsets
let verticalAndFeatureGroupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .absolute(gridBlockSize * 2))
let verticalAndFeatureGroupA = NSCollectionLayoutGroup.horizontal(layoutSize: verticalAndFeatureGroupSize, subitems: isWide ? [verticalGroup, verticalGroup, featureItem, verticalGroup] : [verticalGroup, featureItem])
let verticalAndFeatureGroupB = NSCollectionLayoutGroup.horizontal(layoutSize: verticalAndFeatureGroupSize, subitems: isWide ? [verticalGroup, featureItem, verticalGroup, verticalGroup] : [featureItem, verticalGroup])
let rowGroupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .absolute(gridBlockSize))
let rowGroup = NSCollectionLayoutGroup.horizontal(layoutSize: rowGroupSize, subitem: item, count: isWide ? 5 : 3)
let outerGroupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .absolute(gridBlockSize * 8))
let outerGroup = NSCollectionLayoutGroup.vertical(layoutSize: outerGroupSize, subitems: [verticalAndFeatureGroupA, rowGroup, fullWidthItem, verticalAndFeatureGroupB, rowGroup])
let section = NSCollectionLayoutSection(group: outerGroup)
return section
})
ASCollectionLayoutSection
{ environment in
let isWide = environment.container.effectiveContentSize.width > 500
let gridBlockSize = environment.container.effectiveContentSize.width / (isWide ? 5 : 3)
let gridItemInsets = NSDirectionalEdgeInsets(top: 5, leading: 5, bottom: 5, trailing: 5)
let itemSize = NSCollectionLayoutSize(widthDimension: .absolute(gridBlockSize), heightDimension: .absolute(gridBlockSize))
let item = NSCollectionLayoutItem(layoutSize: itemSize)
item.contentInsets = gridItemInsets
let verticalGroupSize = NSCollectionLayoutSize(widthDimension: .absolute(gridBlockSize), heightDimension: .absolute(gridBlockSize * 2))
let verticalGroup = NSCollectionLayoutGroup.vertical(layoutSize: verticalGroupSize, subitem: item, count: 2)
let featureItemSize = NSCollectionLayoutSize(widthDimension: .absolute(gridBlockSize * 2), heightDimension: .absolute(gridBlockSize * 2))
let featureItem = NSCollectionLayoutItem(layoutSize: featureItemSize)
featureItem.contentInsets = gridItemInsets
let fullWidthItemSize = NSCollectionLayoutSize(widthDimension: .absolute(environment.container.effectiveContentSize.width), heightDimension: .absolute(gridBlockSize * 2))
let fullWidthItem = NSCollectionLayoutItem(layoutSize: fullWidthItemSize)
fullWidthItem.contentInsets = gridItemInsets
let verticalAndFeatureGroupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .absolute(gridBlockSize * 2))
let verticalAndFeatureGroupA = NSCollectionLayoutGroup.horizontal(layoutSize: verticalAndFeatureGroupSize, subitems: isWide ? [verticalGroup, verticalGroup, featureItem, verticalGroup] : [verticalGroup, featureItem])
let verticalAndFeatureGroupB = NSCollectionLayoutGroup.horizontal(layoutSize: verticalAndFeatureGroupSize, subitems: isWide ? [verticalGroup, featureItem, verticalGroup, verticalGroup] : [featureItem, verticalGroup])
let rowGroupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .absolute(gridBlockSize))
let rowGroup = NSCollectionLayoutGroup.horizontal(layoutSize: rowGroupSize, subitem: item, count: isWide ? 5 : 3)
let outerGroupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .absolute(gridBlockSize * 8))
let outerGroup = NSCollectionLayoutGroup.vertical(layoutSize: outerGroupSize, subitems: [verticalAndFeatureGroupA, rowGroup, fullWidthItem, verticalAndFeatureGroupB, rowGroup])
let section = NSCollectionLayoutSection(group: outerGroup)
return section
}
}
......@@ -100,7 +96,7 @@ struct PhotoGridScreen: View
ASCollectionView(
selectedItems: $selectedItems,
sections: [section])
.layoutCompositional(self.layout)
.layout(self.layout)
.navigationBarTitle("Explore", displayMode: .inline)
.navigationBarItems(
trailing:
......
......@@ -29,12 +29,13 @@ struct RemindersScreen: View
var body: some View
{
ASCollectionView {
ASCollectionView
{
ASCollectionViewSection(id: Section.upper, data: self.upperData)
{ model, _ in
GroupLarge(model: model)
}
ASCollectionViewSection(id: Section.list, data: self.lowerData)
{ model, info in
VStack(spacing: 0)
......@@ -57,7 +58,7 @@ struct RemindersScreen: View
Spacer()
}
}
ASCollectionViewSection(id: Section.addNew)
{
GroupSmall(model: self.addNewModel)
......@@ -75,27 +76,31 @@ struct RemindersScreen: View
.padding(.top)
}
}
.layoutCompositional(self.layout)
.contentInsets(.init(top: 20, left: 0, bottom: 20, right: 0))
.alwaysBounceVertical()
.background(Color(.systemGroupedBackground))
.edgesIgnoringSafeArea(.all)
.navigationBarTitle("Reminders", displayMode: .inline)
.layout(self.layout)
.contentInsets(.init(top: 20, left: 0, bottom: 20, right: 0))
.alwaysBounceVertical()
.background(Color(.systemGroupedBackground))
.edgesIgnoringSafeArea(.all)
.navigationBarTitle("Reminders", displayMode: .inline)
}
let groupBackgroundElementID = UUID().uuidString
var layout: ASCollectionViewLayout<Section>
var layout: ASCollectionLayout<Section>
{
ASCollectionViewLayout(interSectionSpacing: 20)
{ section -> ASCollectionViewLayoutSection in
switch section
ASCollectionLayout<Section>(interSectionSpacing: 20)
{ sectionID in
switch sectionID
{
case .upper:
return ASCollectionViewLayoutGrid(layoutMode: .adaptive(withMinItemSize: 165), itemSpacing: 20, lineSpacing: 20, itemSize: .estimated(90))
return .grid(
layoutMode: .adaptive(withMinItemSize: 165),
itemSpacing: 20,
lineSpacing: 20,
itemSize: .estimated(90))
case .list, .addNew:
return ASCollectionViewLayoutCustomCompositionalSection
{ (_, _) -> NSCollectionLayoutSection in
return ASCollectionLayoutSection
{
let itemSize = NSCollectionLayoutSize(
widthDimension: .fractionalWidth(1.0),
heightDimension: .estimated(60))
......@@ -115,7 +120,6 @@ struct RemindersScreen: View
return section
}
// return ASCollectionViewLayoutGrid(layoutMode: .adaptive(withMinItemSize: 220), itemSpacing: 20, lineSpacing: 0, itemSize: .absolute(65))
}
}
.decorationView(GroupBackground.self, forDecorationViewOfKind: groupBackgroundElementID)
......
......@@ -21,7 +21,8 @@ struct TagsScreen: View
}
Text("Tags:")
.font(.title)
ASCollectionView(section:
ASCollectionView(
section:
ASCollectionViewSection(id: 0, data: store.items)
{ item, _ in
Text(item.displayString)
......@@ -29,9 +30,8 @@ struct TagsScreen: View
.padding(5)
.background(Color(.systemGray))
.cornerRadius(5)
}
)
.layoutCustom
})
.layout
{
let fl = AlignedFlowLayout()
fl.estimatedItemSize = UICollectionViewFlowLayout.automaticSize
......
......@@ -42,8 +42,6 @@ struct ExampleView: View {
@State var dataExampleA = (0 ..< 21).map { $0 }
@State var dataExampleB = (0 ..< 15).map { "ITEM \($0)" }
typealias SectionID = Int
var body: some View
{
ASCollectionView
......@@ -63,7 +61,7 @@ struct ExampleView: View {
data: dataExampleB,
dataID: \.self)
{ item, _ in
Color.blue
Color.green
.overlay(
Text("Complex layout - \(item)")
)
......@@ -84,28 +82,23 @@ struct ExampleView: View {
.padding()
}
}
.layoutCompositional(self.layout)
}
var layout: ASCollectionViewLayout<SectionID> {
ASCollectionViewLayout { sectionID -> ASCollectionViewLayoutSection in
switch sectionID {
case 0:
// Here we use one of the predefined convenience layouts
return ASCollectionViewLayoutGrid(layoutMode: .adaptive(withMinItemSize: 100), itemSpacing: 5, lineSpacing: 5, itemSize: .absolute(50))
default:
return self.customSectionLayout
}
}
}
let customSectionLayout = ASCollectionViewLayoutCustomCompositionalSection { (layoutEnvironment, _) -> NSCollectionLayoutSection in
// ...
// Your custom compositional layout section here. For an example see this file: /readmeAssets/SampleUsage.swift
// ...
return section
}
.layout { sectionID in
switch sectionID {
case 0:
// Here we use one of the provided convenience layouts
return .grid(layoutMode: .adaptive(withMinItemSize: 100),
itemSpacing: 5,
lineSpacing: 5,
itemSize: .absolute(50))
default:
return ASCollectionLayoutSection { environment in
// ...
// You could return any custom NSCollectionLayoutSection here. For an example see this file: /readmeAssets/SampleUsage.swift
// ...
}
}
}
}
}
```
......
......@@ -3,6 +3,8 @@
import Combine
import SwiftUI
// MARK: Init for single-section CV
extension ASCollectionView where SectionID == Int
{
/**
......@@ -26,7 +28,7 @@ extension ASCollectionView where SectionID == Int
public struct ASCollectionView<SectionID: Hashable>: UIViewControllerRepresentable
{
public typealias Section = ASCollectionViewSection<SectionID>
public typealias Layout = ASCollectionViewLayout<SectionID>
public typealias Layout = ASCollectionLayout<SectionID>
public var layout: Layout = .default
public var sections: [Section]
public var selectedItems: Binding<[SectionID: IndexSet]>?
......@@ -39,6 +41,8 @@ public struct ASCollectionView<SectionID: Hashable>: UIViewControllerRepresentab
@Environment(\.alwaysBounceVertical) private var alwaysBounceVertical
@Environment(\.editMode) private var editMode
// MARK: Init for multi-section CVs
/**
Initializes a collection view with the given sections
......@@ -50,13 +54,13 @@ public struct ASCollectionView<SectionID: Hashable>: UIViewControllerRepresentab
self.selectedItems = selectedItems
self.sections = sections
}
@inlinable public init(selectedItems: Binding<[SectionID: IndexSet]>? = nil, @SectionArrayBuilder<SectionID> sections: (() -> [Section]))
@inlinable public init(selectedItems: Binding<[SectionID: IndexSet]>? = nil, @SectionArrayBuilder <SectionID> sectionBuilder: () -> [Section])
{
self.selectedItems = selectedItems
self.sections = sections()
self.sections = sectionBuilder()
}
public func makeUIViewController(context: Context) -> AS_CollectionViewController
{
context.coordinator.parent = self
......@@ -107,6 +111,8 @@ public struct ASCollectionView<SectionID: Hashable>: UIViewControllerRepresentab
Coordinator(self)
}
// MARK: Coordinator Class
public class Coordinator: ASCollectionViewCoordinator
{
var parent: ASCollectionView
......@@ -326,14 +332,23 @@ public struct ASCollectionView<SectionID: Hashable>: UIViewControllerRepresentab
parent.sections[indexPath.section].dataSource.insertDragItems(items, at: indexPath)
}
func typeErasedDataForItem(at indexPath: IndexPath) -> Any?
{
guard !indexPath.isEmpty, indexPath.section < parent.sections.endIndex else { return nil }
return parent.sections[indexPath.section].dataSource.getTypeErasedData(for: indexPath)
}
private let queuePrefetch = PassthroughSubject<Void, Never>()
private var prefetchSubscription: AnyCancellable?
private var currentlyPrefetching: Set<IndexPath> = []
}
}
// MARK: Modifer: Custom Delegate
public extension ASCollectionView
{
/// Use this modifier to assign a custom delegate type (subclass of ASCollectionViewDelegate). This allows support for old UICollectionViewLayouts that require a delegate.
func customDelegate(_ delegateInitialiser: @escaping (() -> ASCollectionViewDelegate)) -> Self
{
var cv = self
......@@ -344,6 +359,7 @@ public extension ASCollectionView
internal protocol ASCollectionViewCoordinator: AnyObject
{
func typeErasedDataForItem(at indexPath: IndexPath) -> Any?
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath)
func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath)
func collectionView(_ collectionView: UICollectionView, willDisplaySupplementaryView view: UICollectionReusableView, forElementKind elementKind: String, at indexPath: IndexPath)
......@@ -356,133 +372,7 @@ internal protocol ASCollectionViewCoordinator: AnyObject
func insertItems(_ items: [UIDragItem], at indexPath: IndexPath)
}
open class ASCollectionViewDelegate: NSObject, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout
{
weak var coordinator: ASCollectionViewCoordinator?
open func collectionView(cellShouldSelfSizeHorizontallyForItemAt indexPath: IndexPath) -> Bool
{
return true
}
open func collectionView(cellShouldSelfSizeVerticallyForItemAt indexPath: IndexPath) -> Bool
{
return true
}
open var collectionViewContentInsetAdjustmentBehavior: UIScrollView.ContentInsetAdjustmentBehavior
{
.scrollableAxes
}
public func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath)
{
coordinator?.collectionView(collectionView, willDisplay: cell, forItemAt: indexPath)
}
public func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath)
{
coordinator?.collectionView(collectionView, didEndDisplaying: cell, forItemAt: indexPath)
}
public func collectionView(_ collectionView: UICollectionView, willDisplaySupplementaryView view: UICollectionReusableView, forElementKind elementKind: String, at indexPath: IndexPath)
{
coordinator?.collectionView(collectionView, willDisplaySupplementaryView: view, forElementKind: elementKind, at: indexPath)
}
public func collectionView(_ collectionView: UICollectionView, didEndDisplayingSupplementaryView view: UICollectionReusableView, forElementOfKind elementKind: String, at indexPath: IndexPath)
{
coordinator?.collectionView(collectionView, didEndDisplayingSupplementaryView: view, forElementOfKind: elementKind, at: indexPath)
}
public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath)
{
coordinator?.collectionView(collectionView, didSelectItemAt: indexPath)
}
public func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath)
{
coordinator?.collectionView(collectionView, didDeselectItemAt: indexPath)
}
/*
//REPLACED WITH CUSTOM PREFETCH SOLUTION AS PREFETCH API WAS NOT WORKING FOR COMPOSITIONAL LAYOUT
public func collectionView(_ collectionView: UICollectionView, prefetchItemsAt indexPaths: [IndexPath])
public func collectionView(_ collectionView: UICollectionView, cancelPrefetchingForItemsAt indexPaths: [IndexPath])
*/
}
extension ASCollectionViewDelegate: UICollectionViewDragDelegate, UICollectionViewDropDelegate
{
public func collectionView(_ collectionView: UICollectionView, itemsForBeginning session: UIDragSession, at indexPath: IndexPath) -> [UIDragItem]
{
guard let dragItem = self.coordinator?.dragItem(for: indexPath) else { return [] }
return [dragItem]
}
public func collectionView(_ collectionView: UICollectionView, dropSessionDidUpdate session: UIDropSession, withDestinationIndexPath destinationIndexPath: IndexPath?) -> UICollectionViewDropProposal
{
guard session.localDragSession != nil else
{
return UICollectionViewDropProposal(operation: .forbidden)
}
if collectionView.hasActiveDrag
{
if let destination = destinationIndexPath
{
guard coordinator?.canDrop(at: destination) ?? false else
{
return UICollectionViewDropProposal(operation: .cancel)
}
}
return UICollectionViewDropProposal(operation: .move, intent: .insertAtDestinationIndexPath)
}
else
{
return UICollectionViewDropProposal(operation: .copy, intent: .insertAtDestinationIndexPath)
}
}
public func collectionView(_ collectionView: UICollectionView, performDropWith coordinator: UICollectionViewDropCoordinator)
{
var proposedDestinationIndexPath: IndexPath? = coordinator.destinationIndexPath
if proposedDestinationIndexPath == nil, collectionView.numberOfSections != 0
{
// Get last index path of collection view.
let section = collectionView.numberOfSections - 1
let row = collectionView.numberOfItems(inSection: section)
proposedDestinationIndexPath = IndexPath(row: row, section: section)
}
guard let destinationIndexPath = proposedDestinationIndexPath else { return }
switch coordinator.proposal.operation
{
case .move:
coordinator.items.forEach
{ item in
if let sourceIndex = item.sourceIndexPath
{
self.coordinator?.removeItem(from: sourceIndex)
}
}
self.coordinator?.insertItems(coordinator.items.map { $0.dragItem }, at: destinationIndexPath)
/* self.coordinator?.afterNextUpdate = {
coordinator.items.forEach { (item) in
coordinator.drop(item.dragItem, toItemAt: destinationIndexPath) // This assumption is flawed if dropping multiple items
}
} */
case .copy:
self.coordinator?.insertItems(coordinator.items.map { $0.dragItem }, at: destinationIndexPath)
default:
return
}
}
}
// MARK: Custom Prefetching Implementation
extension ASCollectionView.Coordinator
{
......@@ -596,6 +486,7 @@ public class AS_CollectionViewController: UIViewController
public override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator)
{
super.viewWillTransition(to: size, with: coordinator)
// The following is a workaround to fix the interface rotation animation under SwiftUI
view.frame = CGRect(origin: view.frame.origin, size: size)
coordinator.animate(alongsideTransition: { _ in
self.view.setNeedsLayout()
......@@ -607,49 +498,50 @@ public class AS_CollectionViewController: UIViewController
public override func viewSafeAreaInsetsDidChange()
{
super.viewSafeAreaInsetsDidChange()
// The following is a workaround to fix the interface rotation animation under SwiftUI
collectionViewLayout.invalidateLayout()
}
}
public extension ASCollectionView
{
func layoutCompositional(_ layout: ASCollectionViewLayout<SectionID>) -> Self
func layout(_ layout: Layout) -> Self
{
var this = self
this.layout = layout
return this
}
func layoutCompositional(
func layout(
scrollDirection: UICollectionView.ScrollDirection = .vertical,
interSectionSpacing: CGFloat = 10,
layoutPerSection: @escaping ((_ sectionID: SectionID) -> ASCollectionViewLayoutSection)) -> Self
layoutPerSection: @escaping CompositionalLayout<SectionID>) -> Self
{
var this = self
this.layout = ASCollectionViewLayout(
this.layout = Layout(
scrollDirection: scrollDirection,
interSectionSpacing: interSectionSpacing,
layoutPerSection: layoutPerSection)
return this
}
func layoutCompositional(
func layout(
scrollDirection: UICollectionView.ScrollDirection = .vertical,
interSectionSpacing: CGFloat = 10,
layout: @escaping (() -> ASCollectionViewLayoutSection)) -> Self
layout: @escaping CompositionalLayoutIgnoringSections) -> Self
{
var this = self
this.layout = ASCollectionViewLayout(
this.layout = Layout(
scrollDirection: scrollDirection,
interSectionSpacing: interSectionSpacing,
layout: layout)
return this
}
func layoutCustom(customLayout: @escaping (() -> UICollectionViewLayout)) -> Self
func layout(customLayout: @escaping (() -> UICollectionViewLayout)) -> Self
{
var this = self
this.layout = ASCollectionViewLayout(customLayout: customLayout)
this.layout = Layout(customLayout: customLayout)
return this
}
}
// ASCollectionView. Created by Apptek Studios 2019
import Foundation
import SwiftUI
/// ASCollectionViewDelegate: Subclass this to create a custom delegate (eg. for supporting UICollectionViewLayouts that default to using the collectionView delegate)
open class ASCollectionViewDelegate: NSObject, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout
{
weak var coordinator: ASCollectionViewCoordinator?
public func getDataForItem(at indexPath: IndexPath) -> Any?
{
coordinator?.typeErasedDataForItem(at: indexPath)
}
public func getDataForItem<T>(at indexPath: IndexPath) -> T?
{
coordinator?.typeErasedDataForItem(at: indexPath) as? T
}
open func collectionView(cellShouldSelfSizeHorizontallyForItemAt indexPath: IndexPath) -> Bool
{
return true
}
open func collectionView(cellShouldSelfSizeVerticallyForItemAt indexPath: IndexPath) -> Bool
{
return true
}
open var collectionViewContentInsetAdjustmentBehavior: UIScrollView.ContentInsetAdjustmentBehavior
{
.scrollableAxes
}
public func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath)
{
coordinator?.collectionView(collectionView, willDisplay: cell, forItemAt: indexPath)
}
public func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath)
{
coordinator?.collectionView(collectionView, didEndDisplaying: cell, forItemAt: indexPath)
}
public func collectionView(_ collectionView: UICollectionView, willDisplaySupplementaryView view: UICollectionReusableView, forElementKind elementKind: String, at indexPath: IndexPath)
{
coordinator?.collectionView(collectionView, willDisplaySupplementaryView: view, forElementKind: elementKind, at: indexPath)
}
public func collectionView(_ collectionView: UICollectionView, didEndDisplayingSupplementaryView view: UICollectionReusableView, forElementOfKind elementKind: String, at indexPath: IndexPath)
{
coordinator?.collectionView(collectionView, didEndDisplayingSupplementaryView: view, forElementOfKind: elementKind, at: indexPath)
}
public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath)
{
coordinator?.collectionView(collectionView, didSelectItemAt: indexPath)
}
public func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath)
{
coordinator?.collectionView(collectionView, didDeselectItemAt: indexPath)
}
/*
//REPLACED WITH CUSTOM PREFETCH SOLUTION AS PREFETCH API WAS NOT WORKING FOR COMPOSITIONAL LAYOUT
public func collectionView(_ collectionView: UICollectionView, prefetchItemsAt indexPaths: [IndexPath])
public func collectionView(_ collectionView: UICollectionView, cancelPrefetchingForItemsAt indexPaths: [IndexPath])
*/
}
extension ASCollectionViewDelegate: UICollectionViewDragDelegate, UICollectionViewDropDelegate
{
public func collectionView(_ collectionView: UICollectionView, itemsForBeginning session: UIDragSession, at indexPath: IndexPath) -> [UIDragItem]
{
guard let dragItem = self.coordinator?.dragItem(for: indexPath) else { return [] }
return [dragItem]
}
public func collectionView(_ collectionView: UICollectionView, dropSessionDidUpdate session: UIDropSession, withDestinationIndexPath destinationIndexPath: IndexPath?) -> UICollectionViewDropProposal
{
guard session.localDragSession != nil else
{
return UICollectionViewDropProposal(operation: .forbidden)
}
if collectionView.hasActiveDrag
{
if let destination = destinationIndexPath
{
guard coordinator?.canDrop(at: destination) ?? false else
{
return UICollectionViewDropProposal(operation: .cancel)
}
}
return UICollectionViewDropProposal(operation: .move, intent: .insertAtDestinationIndexPath)
}
else
{
return UICollectionViewDropProposal(operation: .copy, intent: .insertAtDestinationIndexPath)
}
}
public func collectionView(_ collectionView: UICollectionView, performDropWith coordinator: UICollectionViewDropCoordinator)
{
var proposedDestinationIndexPath: IndexPath? = coordinator.destinationIndexPath
if proposedDestinationIndexPath == nil, collectionView.numberOfSections != 0
{
// Get last index path of collection view.
let section = collectionView.numberOfSections - 1
let row = collectionView.numberOfItems(inSection: section)
proposedDestinationIndexPath = IndexPath(row: row, section: section)
}
guard let destinationIndexPath = proposedDestinationIndexPath else { return }
switch coordinator.proposal.operation
{
case .move:
coordinator.items.forEach
{ item in
if let sourceIndex = item.sourceIndexPath
{
self.coordinator?.removeItem(from: sourceIndex)
}
}
self.coordinator?.insertItems(coordinator.items.map { $0.dragItem }, at: destinationIndexPath)
/* self.coordinator?.afterNextUpdate = {
coordinator.items.forEach { (item) in
coordinator.drop(item.dragItem, toItemAt: destinationIndexPath) // This assumption is flawed if dropping multiple items
}
} */
case .copy:
self.coordinator?.insertItems(coordinator.items.map { $0.dragItem }, at: destinationIndexPath)
default:
return
}
}
}
This diff is collapsed.
......@@ -8,6 +8,7 @@ internal protocol ASSectionDataSourceProtocol
func getIndexPaths(withSectionIndex sectionIndex: Int) -> [IndexPath]
func getUniqueItemIDs<SectionID: Hashable>(withSectionID sectionID: SectionID) -> [ASCollectionViewItemUniqueID]
func configureHostingController(reusingController: ASHostingControllerProtocol?, forItemID itemID: ASCollectionViewItemUniqueID, isSelected: Bool) -> ASHostingControllerProtocol?
func getTypeErasedData(for indexPath: IndexPath) -> Any?
func onAppear(_ indexPath: IndexPath)
func onDisappear(_ indexPath: IndexPath)
func prefetch(_ indexPaths: [IndexPath])
......@@ -86,6 +87,12 @@ internal struct ASSectionDataSource<Data, DataID, Content>: ASSectionDataSourceP
}
}
func getTypeErasedData(for indexPath: IndexPath) -> Any?
{
guard indexPath.item < data.endIndex else { return nil }
return data[indexPath.item]
}
func getIndexPaths(withSectionIndex sectionIndex: Int) -> [IndexPath]
{
data.indices.map { IndexPath(item: $0, section: sectionIndex) }
......
......@@ -52,12 +52,12 @@ public struct ASTableView<SectionID: Hashable>: UIViewControllerRepresentable
self.selectedItems = selectedItems
self.sections = sections
}
@inlinable public init(mode: UITableView.Style = .plain, selectedItems: Binding<[SectionID: IndexSet]>? = nil, @SectionArrayBuilder<SectionID> sections: (() -> [Section]))
@inlinable public init(mode: UITableView.Style = .plain, selectedItems: Binding<[SectionID: IndexSet]>? = nil, @SectionArrayBuilder <SectionID> sectionBuilder: () -> [Section])
{
self.mode = mode
self.selectedItems = selectedItems
self.sections = sections()
self.sections = sectionBuilder()
}
public func makeUIViewController(context: Context) -> UITableViewController
......@@ -201,7 +201,7 @@ public struct ASTableView<SectionID: Hashable>: UIViewControllerRepresentable
// APPLY CHANGES (ADD/REMOVE CELLS) AFTER REFRESHING CELLS
dataSource?.apply(snapshot, animatingDifferences: refreshExistingCells)
updateSelectionBindings(tv)
DispatchQueue.main.async
{
self.checkIfReachedBottom(tv)
......
......@@ -8,30 +8,29 @@ public struct SectionArrayBuilder<SectionID> where SectionID: Hashable
{
public typealias Section = ASCollectionViewSection<SectionID>
public typealias Output = [Section]
public static func buildBlock(_ section: Section) -> Output
{
[section]
}
public static func buildBlock(_ sections: Section...) -> Output
{
sections
}
public static func buildEither(first: Section) -> Output
{
[first]
}
public static func buildEither(second: Section) -> Output
{
[second]
}
public static func buildIf(_ item: Section?) -> Output
{
return [item].compactMap { $0 }
}
}
......@@ -8,8 +8,6 @@ struct ExampleView: View
@State var dataExampleA = (0..<21).map { $0 }
@State var dataExampleB = (0..<15).map { "ITEM \($0)" }
typealias SectionID = Int
var body: some View
{
ASCollectionView
......@@ -29,7 +27,7 @@ struct ExampleView: View
data: dataExampleB,
dataID: \.self)
{ item, _ in
Color.blue
Color.green
.overlay(
Text("Complex layout - \(item)")
)
......@@ -50,60 +48,57 @@ struct ExampleView: View
.padding()
}
}
.layoutCompositional(self.layout)
}
var layout: ASCollectionViewLayout<SectionID>
{
ASCollectionViewLayout
{ sectionID -> ASCollectionViewLayoutSection in
switch sectionID
{
case 0:
return ASCollectionViewLayoutGrid(layoutMode: .adaptive(withMinItemSize: 100), itemSpacing: 5, lineSpacing: 5, itemSize: .absolute(50))
default:
return self.customSectionLayout
}
}
}
let customSectionLayout = ASCollectionViewLayoutCustomCompositionalSection
{ (layoutEnvironment, _) -> NSCollectionLayoutSection in
let isWide = layoutEnvironment.container.effectiveContentSize.width > 500
let gridBlockSize = layoutEnvironment.container.effectiveContentSize.width / (isWide ? 5 : 3)
let gridItemInsets = NSDirectionalEdgeInsets(top: 5, leading: 5, bottom: 5, trailing: 5)
let itemSize = NSCollectionLayoutSize(widthDimension: .absolute(gridBlockSize), heightDimension: .absolute(gridBlockSize))
let item = NSCollectionLayoutItem(layoutSize: itemSize)
item.contentInsets = gridItemInsets
let verticalGroupSize = NSCollectionLayoutSize(widthDimension: .absolute(gridBlockSize), heightDimension: .absolute(gridBlockSize * 2))
let verticalGroup = NSCollectionLayoutGroup.vertical(layoutSize: verticalGroupSize, subitem: item, count: 2)
.layout
{ sectionID in
switch sectionID
{
case 0:
return .grid(
layoutMode: .adaptive(withMinItemSize: 100),
itemSpacing: 5,
lineSpacing: 5,
itemSize: .absolute(50))
default:
return ASCollectionLayoutSection
{ environment in
let isWide = environment.container.effectiveContentSize.width > 500
let gridBlockSize = environment.container.effectiveContentSize.width / (isWide ? 5 : 3)
let gridItemInsets = NSDirectionalEdgeInsets(top: 5, leading: 5, bottom: 5, trailing: 5)
let itemSize = NSCollectionLayoutSize(widthDimension: .absolute(gridBlockSize), heightDimension: .absolute(gridBlockSize))
let item = NSCollectionLayoutItem(layoutSize: itemSize)
item.contentInsets = gridItemInsets
let verticalGroupSize = NSCollectionLayoutSize(widthDimension: .absolute(gridBlockSize), heightDimension: .absolute(gridBlockSize * 2))
let verticalGroup = NSCollectionLayoutGroup.vertical(layoutSize: verticalGroupSize, subitem: item, count: 2)
let featureItemSize = NSCollectionLayoutSize(widthDimension: .absolute(gridBlockSize * 2), heightDimension: .absolute(gridBlockSize * 2))
let featureItem = NSCollectionLayoutItem(layoutSize: featureItemSize)
featureItem.contentInsets = gridItemInsets
let featureItemSize = NSCollectionLayoutSize(widthDimension: .absolute(gridBlockSize * 2), heightDimension: .absolute(gridBlockSize * 2))
let featureItem = NSCollectionLayoutItem(layoutSize: featureItemSize)
featureItem.contentInsets = gridItemInsets
let verticalAndFeatureGroupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .absolute(gridBlockSize * 2))
let verticalAndFeatureGroupA = NSCollectionLayoutGroup.horizontal(layoutSize: verticalAndFeatureGroupSize, subitems: isWide ? [verticalGroup, verticalGroup, featureItem, verticalGroup] : [verticalGroup, featureItem])
let verticalAndFeatureGroupB = NSCollectionLayoutGroup.horizontal(layoutSize: verticalAndFeatureGroupSize, subitems: isWide ? [verticalGroup, featureItem, verticalGroup, verticalGroup] : [featureItem, verticalGroup])
let verticalAndFeatureGroupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .absolute(gridBlockSize * 2))
let verticalAndFeatureGroupA = NSCollectionLayoutGroup.horizontal(layoutSize: verticalAndFeatureGroupSize, subitems: isWide ? [verticalGroup, verticalGroup, featureItem, verticalGroup] : [verticalGroup, featureItem])
let verticalAndFeatureGroupB = NSCollectionLayoutGroup.horizontal(layoutSize: verticalAndFeatureGroupSize, subitems: isWide ? [verticalGroup, featureItem, verticalGroup, verticalGroup] : [featureItem, verticalGroup])
let rowGroupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .absolute(gridBlockSize))
let rowGroup = NSCollectionLayoutGroup.horizontal(layoutSize: rowGroupSize, subitem: item, count: isWide ? 5 : 3)
let rowGroupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .absolute(gridBlockSize))
let rowGroup = NSCollectionLayoutGroup.horizontal(layoutSize: rowGroupSize, subitem: item, count: isWide ? 5 : 3)
let outerGroupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .absolute(gridBlockSize * 6))
let outerGroup = NSCollectionLayoutGroup.vertical(layoutSize: outerGroupSize, subitems: [verticalAndFeatureGroupA, rowGroup, verticalAndFeatureGroupB, rowGroup])
let outerGroupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .absolute(gridBlockSize * 6))
let outerGroup = NSCollectionLayoutGroup.vertical(layoutSize: outerGroupSize, subitems: [verticalAndFeatureGroupA, rowGroup, verticalAndFeatureGroupB, rowGroup])
let section = NSCollectionLayoutSection(group: outerGroup)
let section = NSCollectionLayoutSection(group: outerGroup)
let supplementarySize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .estimated(50))
let headerSupplementary = NSCollectionLayoutBoundarySupplementaryItem(
layoutSize: supplementarySize,
elementKind: UICollectionView.elementKindSectionHeader,
alignment: .top)
let footerSupplementary = NSCollectionLayoutBoundarySupplementaryItem(
layoutSize: supplementarySize,
elementKind: UICollectionView.elementKindSectionFooter,
alignment: .bottom)
section.boundarySupplementaryItems = [headerSupplementary, footerSupplementary]
return section
let supplementarySize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .estimated(50))
let headerSupplementary = NSCollectionLayoutBoundarySupplementaryItem(
layoutSize: supplementarySize,
elementKind: UICollectionView.elementKindSectionHeader,
alignment: .top)
let footerSupplementary = NSCollectionLayoutBoundarySupplementaryItem(
layoutSize: supplementarySize,
elementKind: UICollectionView.elementKindSectionFooter,
alignment: .bottom)
section.boundarySupplementaryItems = [headerSupplementary, footerSupplementary]
return section
}
}
}
}
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment