Skip to content

stshelton/YCollectionView

Repository files navigation

Swift Version Build Status License

Platform PRs Welcome

YCollectionView


YCollectionView is a elegant swiftUI CollectionView built off UICollectionView. It was designed to solve performance issues associated with SwiftUI stack and scroll view nesting by achieving elegant scrolling and cell resuablity that UICollectionVew provides. It allows cells and supplementary views to be built in SwiftUI and provides built in basic paganation logic, pull to refresh and on cell tap functionally. YCollectionView was built off https://github.com/defagos/SwiftUICollection sample project for his article for Building a Collection For SwiftUI

Features

  • Pagination/Infinite Scrolling vertically and horizontally
  • Cells and Supplementary views are built with swiftUI Views
  • Fully customizable sections using compositional layouts
  • Uses diffable data source as collectionViews data source
  • Built in pull to refresh added to view

Requirements

  • iOS 14.0+
  • Xcode 12.0

Installation

YCollectionview is available via Swift Package Manger

Using Xcode 12, go to File -> Swift Packages -> Add Package Dependecy and enter and enter https://github.com/stshelton/YCollectionView

Usage example

Heres the most basic implentation of YCollectionView. If you'd like to see a more complex implentation that shows pagination refer to the Sample Project

• Create Cell and Section Data that conform to hashable protocol

     struct BasicDemoData: Hashable{
        var id: UUID = UUID()
        var title: String
    }
    
    struct BasicSectionData: Hashable{
        var id: UUID = UUID()
        var extraSectionInfo: String = ""
    }

• Create CollectionViewSection, which contains an array of hashable objects for cells(BasicDemoData) and a hashable object for section(BasicSectionData)

    //section
    typealias Section = CollectionViewSection<AnyHashable, AnyHashable>
    
    //basic Section
    var basicSection = Section(section: BasicSectionData(), items: [BasicDemoData(title: "Test 1"), 
    BasicDemoData(title: "Test 2"), BasicDemoData(title: "Test 3"), BasicDemoData(title: "Test 4")])

• Create Basic NSCollectionLayoutSection for compositional layout

       private func CreateBasicLayoutSection()-> NSCollectionLayoutSection{
        //1
        let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
        heightDimension: .fractionalHeight(1.0))
        let item = NSCollectionLayoutItem(layoutSize: itemSize)
        //2
        let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
        heightDimension: .fractionalHeight(0.3))
        //3
        let group = NSCollectionLayoutGroup.vertical(layoutSize: groupSize,subitems: [item])
        let section = NSCollectionLayoutSection(group: group)
        
        return section
    }

• Full Example

import SwiftUI
import YCollectionView

struct BasicYCollectionViewImplementation: View {
    
    //section
    typealias Section = CollectionViewSection<AnyHashable, AnyHashable>
    
    //basic Section
    var basicSection = Section(section: BasicSectionData(), items: [BasicDemoData(title: "Test 1"), BasicDemoData(title: "Test 2"),
    BasicDemoData(title: "Test 3"), BasicDemoData(title: "Test 4")])
    
    
    struct BasicDemoData: Hashable{
        var id: UUID = UUID()
        var title: String
    }
    
    struct BasicSectionData: Hashable{
        var id: UUID = UUID()
        var extraSectionInfo: String = ""
    }
    
    
    var body: some View {
        CollectionView(rows: [basicSection], collectionViewBackgroundColor: UIColor.white, addPullToRefresh: true) { sectionIndex, layoutEnviroment in
            return CreateBasicLayoutSection()
        } cell: { indexPath, cellData in
            switch cellData{
            case let basicCellData as BasicDemoData:
                VStack{
                    Text(basicCellData.title)
                }
                
            default:
                EmptyView()
            }
        } supplementaryView: { kind, indexPath, sectionData in
            //header and footer views
        } loadMoreCells: { loadingSection in
            //logic to load more cells within section
        } loadMoreSections: {
            //logic to load more sections
        } clickedDetailPage: { item in
            //current cell clicked and data associated with cell
        } pullToRefresh: { refreshControl in
            DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
                refreshControl.endRefreshing()
            }
        }

    }
    
    private func CreateBasicLayoutSection()-> NSCollectionLayoutSection{
        //1
        let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
        heightDimension: .fractionalHeight(1.0))
        let item = NSCollectionLayoutItem(layoutSize: itemSize)
        //2
        let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),heightDimension: .fractionalHeight(0.3))
        //3
        let group = NSCollectionLayoutGroup.vertical(layoutSize: groupSize,subitems: [item])
        let section = NSCollectionLayoutSection(group: group)
        
        return section
    }
}

Customize Loading Data

YCollectionView uses willDisplay method of UICollectionViewDelegate to check if last cell within data source is or inherits from LoadMoreCellsData object. If you'd like to add more data to the LoadMoreCellsData create a new object that inherits from LoadMoreCellsData and add aditional data needed within that class.

class OverrideLoadingCell: LoadMoreCellsData{
    var extraData: String
    
    init(extraData: String){
        self.extraData = extraData
        super.init()
    }
}

Accessing the data

  cell: { indexPath, cellData in
                switch cellData {
                case let demoCellData as DemoCellData:
                    BasicCellView(demoCellData: demoCellData)
                case let loadingMoreData as OverrideLoadingCell:
                    LoadingCellView(loadingData: loadingMoreData)
                default:
                    EmptyView()
                }
            } 

The same is true with loading more sections. YCollectionView uses willDisplay method of UICollectionViewDelegate to check if last cell in section is or inherits from LoadMoreSectionsCellData. With a section intented to load more sections I would only pass in one cell which is or inherits from LoadMoreSectionsCellData.

class OverrideSectionLoadingCell: LoadMoreSectionsCellData{
    var extraData: String = ""
    
    init(extraData: String){
      self.extraData = extraData
    }
}

Accessing the data

  cell: { indexPath, cellData in
                switch cellData {
                case let demoCellData as DemoCellData:
                    BasicCellView(demoCellData: demoCellData)
                case let loadingMoreData as OverrideLoadingCell:
                    LoadingCellView(loadingData: loadingMoreData)
                case let loadingMoreSections as OverrideSectionLoadingCell:
                    LoadingNewSectionCellView(loadingSectionData: loadingMoreSections)
                default:
                    EmptyView()
                }
            } 

Contribute

We would love you for the contribution to YCollectionView, check the LICENSE file for more info.

Meta

Spencer Shelton - Stshelton1993@example.com

Distributed under the MIT license. See LICENSE for more information.

https://github.com/stshelton

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Packages

No packages published