Giter VIP home page Giter VIP logo

dtcollectionviewmanager's Introduction

CI codecov.io CocoaPod platform CocoaPod version Swift Package Manager compatible Packagist

DTCollectionViewManager

Features

  • Powerful mapping system between data models and cells, headers and footers
  • Automatic datasource and interface synchronization.
  • Flexible Memory/CoreData/Realm/diffable datasource storage options
  • Powerful compile-time safe events system, that covers all of UICollectionView delegate methods
  • Views created from code, XIB, or storyboard, automatic registration and dequeue
  • Can be used with UICollectionViewController, or UIViewController with UICollectionView
  • Built-in support for iOS 14 UICollectionView.CellRegistration and content configuration
  • Unified syntax with DTTableViewManager
  • Complete documentation
  • API Reference

Read more about latest 11.0 release and integration with SwiftUI!

Requirements

  • Xcode 12+
  • iOS 11.0+ / tvOS 11.0+ / macCatalyst 13.0+
  • Swift 5.3+

If you need Xcode 11 support or Swift 4...Swift 5.2, or iOS 8...iOS 10 support, you can use 7.x releases.

Installation

Swift Package Manager

Add package into Xcode Project settings -> Swift Packages

pod 'DTCollectionViewManager', '~> 11.0.0-beta.1'

Quick start

Let's say you have an array of Posts you want to display in UICollectionView. To quickly show them using DTCollectionViewManager, here's what you need to do:

  1. Create UICollectionViewCell subclass, let's say PostCell and adopt ModelTransfer protocol:
class PostCell : UICollectionViewCell, ModelTransfer {
    func update(with model: Post) {
        // Fill your cell with actual data
    }
}
  1. In your view controller:
class PostsViewController: UICollectionViewController, DTCollectionViewManageable {

    override func viewDidLoad() {
        super.viewDidLoad()

        // Register PostCell to be used with this controller's collection view
        manager.register(PostCell.self)

        // Populate datasource
        manager.memoryStorage.setItems(posts)
    }
}    

Make sure your UICollectionView outlet is wired to your class (or use UICollectionViewController subclass). If you have a PostCell.xib file, it will be automatically used for dequeueing PostCell.

  1. That's it! It's that easy!

Of course, cool stuff does not stop there, framework supports all datasource and delegate methods as closures, conditional mappings and much much more! Choose what interests you in the next section of readme.

Burning questions

Starter pack
Advanced

Sample code and documentation

Thanks

  • Alexey Belkevich for providing initial implementation of CellFactory.
  • Michael Fey for providing insight into NSFetchedResultsController updates done right.
  • Nickolay Sheika for great feedback, that helped shaping 3.0 release and future direction of the library.
  • Artem Antihevich for great discussions about Swift generics and type capturing.

dtcollectionviewmanager's People

Contributors

artfeel avatar biow0lf avatar bitdeli-chef avatar dentelezhkin avatar orkenstein avatar readmecritic avatar rengate avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

dtcollectionviewmanager's Issues

Quick start updates

DTCollectionViewManageable doesn't work with outlets like @IBOutlet weak var collectionView: UICollectionView!. Please, updates the Quick start documentation to use the following protocol DTCollectionViewNonOptionalManageable instead of the DTCollectionViewManageable for @outlet. Thanks.

Crash with nil header

In your method:

- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView
           viewForSupplementaryElementOfKind:(NSString *)kind
                                 atIndexPath:(NSIndexPath *)indexPath
{
    UICollectionReusableView <DTModelTransfer> *view = nil;
    id supplementary = nil;
    if ([self.storage respondsToSelector:@selector(supplementaryModelOfKind:forSectionIndex:)])
    {
        supplementary = [self.storage supplementaryModelOfKind:kind forSectionIndex:indexPath.section];
    }

    if (supplementary)
    {
        view = [self.factory supplementaryViewOfKind:kind
                                             forItem:supplementary
                                         atIndexPath:indexPath];
        [view updateWithModel:supplementary];
    }

    return view;
}

If no reusable view was registered, you will return nil. Since IOS7 it's create a crash.

Items get messed up when sections are added

Scrolling and reusing items seems to work fine when there are no sections present, but as soon as I add one, the list get laggy and jumpy. Also, the second section somehow overlaps some of the items.

The code (in a view, inside awakeFromNib):

manager.registerHeader(TimeHeader.self) { $0.condition = .section(0) } manager.registerHeader(TimeSectionHeader.self) { $0.condition = .section(1) } storage.setSectionHeaderModel(TimeHeaderModel(), forSection: 0) storage.setSectionHeaderModel(TimeSectionHeaderModel(timeDay: .monday), forSection: 1)

Then I'm simply adding models to storage as usual.

registerCellClass with no nibs

Currently I'm not using nibs nor storyboard, thus I just call registerCellClass:. When the CollectionView tries to dequeue a cell it raises an exception saying that no cell class has been registered.
The fix is easy, I just need to call collectionView?.registerClass(CellClass.self, forCellWithReuseIdentifier: "<reuse identifier that would be generated with reflection>") from my view controller.
This kind of stuff could go in the library itself, in my opinion. Also because if one day you change identifier, my code breaks.

What do you think?
Before I open a PR, I'd like to know if it would be enough to add the code above to an else clause in the registerCellClass: method or if there's no else clause because that's needed for storyboards compatibility: if no nib exists it means that the view is in the storyboard?

If that's the case, I propose to handle the pure (code only) case in the method registerCellClass: and add a new method registerCellClassWithNoStoryboardNorNib: that has the current implementation (suggestions for method names are appreciated!!).

(I'll obviously update the cousin registerSupplementaryClass: forKind: method)

UICollectionView performBatchUpdates can trigger a crash if the collection view is flagged for layout

There's is a known issue with UICollectionView, that crashes application if reloadData call was sent prior to performBatchUpdates method.

Radar: https://openradar.appspot.com/28167779

Sample: https://github.com/PSPDFKit-labs/radar.apple.com/tree/master/28167779%20-%20CollectionViewBatchingIssue

Known workarounds - in terms of DTCollectionViewManager:

  • Do not call reloadData method manually, use other methods on MemoryStorage or CoreDataStorage
  • Try to group updates into single update block vs calling many blocks all at once - If needed drop to SectionModel level of abstraction.
  • You can implement your own CollectionViewUpdater, that calls reloadData instead of calling performBatchUpdates, however this will cost you animations, that will not be performed.

Any help or ideas how to implement maintainable solution are welcomed!

Specifying Different Supplementary Views Per Section

How can I specify that I want a certain supplementary view to show up in a certain section of the collectionView?

Maybe i am missing where in the docs I can do this

e.x
Section 1 = HeaderViewOne
Section 2 = HeaderViewTwo
.
.
.
Section n = HeaderViewN

possible to have multiple model-types for a cell?

I'm wondering if there is a way to use different model-types for a collection-view cell.

I have two types: Book and Movie. Each type has a title, and an associated image.

What I'd like to do is use the same UICollectionViewCell (let's call it GenericCell) and register it so that a different type can be used, besides the one that is defined in the cell's ModelType.

Barring that, maybe a runtime transform?... so I could see "Oh the model is a Book, so transform it to a ImageTitle object". Then the ModelType and update routine would use ImageTitle even though the object from the data-source was actually a Book or Movie or whatever.

can you set more than one `configure` block for a cell class?

I was trying to do something where I was calling configure twice on the same cell class.

Looking at the configure code, it calls a routine named appendReaction, which made me think that I could do this. Ordering of the two blocks doesn't matter. But I noticed, while stepping through, that delegateWasReset gets called when the reaction is appended. The end result is that the second configure block is not called.

Anyway, just wondering if 2 configure blocks can be added to the same cell-class, and if so, how? If not, then I just need to know that.

Clearer documentation examples

Took me a bit to time to get the order of setup & that I need to perform fetch on my NSFetchedResultsController

Here's my code:

    override func viewDidLoad() {
        super.viewDidLoad()

        let fetchRequest = NSFetchRequest(entityName: "Photograph")
        //fetchRequest.predicate = self.predicate
        fetchRequest.sortDescriptors = [NSSortDescriptor(key: "primaryKey", ascending: true)]

        let ctx = RKObjectManager.sharedManager().managedObjectStore.persistentStoreManagedObjectContext
        let frc = NSFetchedResultsController(fetchRequest: fetchRequest,
                                             managedObjectContext: ctx,
                                             sectionNameKeyPath: nil,
                                             cacheName: nil)

        do {
            try frc.performFetch()
        } catch {
            let fetchError = error as NSError
            print("\(fetchError), \(fetchError.userInfo)")
        }

        self.manager.storage = CoreDataStorage(fetchedResultsController: frc)
        self.manager.startManagingWithDelegate(self)
        self.manager.registerNiblessCellClass(GalleryImageCell)
    }

Collection view lags

Hi!
I have one image in each cell and while scrolling collection view lags. Scrolling collection view jams loading cells.
Any solution for this?

Generic parameter 'T' could not be inferred.

After I updated to the swift 3 version (5.0.0-beta.2), this error shows up:

Generic parameter 'T' could not be inferred.

What I'm trying to do with my collection view is that I want to show a bunch of colors for the user to choose from. Here is the cell class:

class ColorCell: UICollectionViewCell, ModelTransfer {
    typealias ModelType = UIColor

    func update(with model: UIColor) {
        layer.cornerRadius = self.w / 2
        layer.borderWidth = 1.5
        self.backgroundColor = model
    }
}

Then, I wrote a method with this signature:

func selectedColor(_ cell: ColorCell, color: UIColor, indexPath: IndexPath) {

}

So far so good. But the error appears on this line:

manager.didSelect(ColorCell.self, ColorSelectorController.selectedColor)

It says that T cannot be inferred! But obviously T is ColorCell!

Crash when inserting items

CollectionViewManager crashes when it checks if it shouldReloadCollectionViewToPreventInsertFirstItemIssueForUpdate

In particular, I'm asynchronously adding new items to the manager:

private func contentLoaded(resources: [Container]?, error: ErrorType?) {
    print("content loaded")

    if resources != nil {
        manager.memoryStorage.addItems(resources!)
    }
}

The manager then checks:

for indexPath in update.insertedRowIndexPaths {
    if self.collectionView.numberOfItemsInSection(indexPath.section) == 0 {
        shouldReload = true
        break
    }
}

And crashes when calls self.collectionView.numberOfItemsInSection(indexPath.section) (in that moment, there were 0 sections with 0 items).
Removing those lines make the app work correctly.

I run the app on the iOS 9 Simulator.

Exception logged:

*** Assertion failure in -[UICollectionViewData numberOfItemsInSection:], /BuildRoot/Library/Caches/com.apple.xbs/Sources/UIKit_Sim/UIKit-3505.16/UICollectionViewData.m:566
2015-10-02 17:48:42.212 Sales App[82499:1913317] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'request for number of items in section 0 when there are only 0 sections in the collection view'

Crash if header/footer sizes set in Layout

For example layout:

_layout = [UICollectionViewFlowLayout new]; 
        _layout.scrollDirection = UICollectionViewScrollDirectionVertical;
        _layout.minimumLineSpacing = 10.f;
        _layout.minimumInteritemSpacing = 0;
        const CGFloat kHeaderHeight = 25.0f;
        _layout.headerReferenceSize = CGSizeMake(self.collectionView.width, 50.f);

Methods -

- (CGSize)collectionView:(UICollectionView *)collectionView
                 layout:(UICollectionViewFlowLayout *)collectionViewLayout
referenceSizeForHeaderInSection:(NSInteger)sectionNumber
{
    BOOL supplementaryModelNotNil = NO;
    if ([self.storage respondsToSelector:@selector(supplementaryModelOfKind:forSectionIndex:)])
    {
        supplementaryModelNotNil = ([self.storage supplementaryModelOfKind:UICollectionElementKindSectionHeader
                                                           forSectionIndex:sectionNumber]!=nil);
    }
    return supplementaryModelNotNil ? collectionViewLayout.headerReferenceSize : CGSizeZero;
}

- (CGSize)collectionView:(UICollectionView *)collectionView
                 layout:(UICollectionViewFlowLayout *)collectionViewLayout
referenceSizeForFooterInSection:(NSInteger)sectionNumber
{
    BOOL supplementaryModelNotNil = NO;
    if ([self.storage respondsToSelector:@selector(supplementaryModelOfKind:forSectionIndex:)])
    {
        supplementaryModelNotNil = ([self.storage supplementaryModelOfKind:UICollectionElementKindSectionFooter
                                                           forSectionIndex:sectionNumber]!=nil);
    }
    return supplementaryModelNotNil ? collectionViewLayout.footerReferenceSize : CGSizeZero;
}

Will never get called, so trick with CGSizeZero will not help, and app will crash.

How to get cell.indexPath in ModelTransfer#updateWithModel()?

updateWithModel() has an only parameter for the data model. How can I get the indexPath for the cell? UICollectionView#indexPathForCell doesn't work cause it returns nil when the cell is not visible.
DTCollectionViewManager#configureCell() doesn't get called on cell's dequeuing.

How to establish @IBOutlet collectionView in Swift?

In order to use DTCollectionViewManager i'm trying to implement desired functionality in my DTCollectionViewController sub-class. However, I can not add @IBOutlet for collectionView.
What is best practice?
The following code:
@IBOutlet var collectionView: UICollectionView!

gives me compiler error:

Property 'collectionView' with type 'UICollectionView!' cannot override a property with type           'UICollectionView'
Setter for 'collectionView' with Objective-C selector 'setCollectionView:' conflicts with setter for       'collectionView' from superclass 'DTCollectionViewController' with the same Objective-C selector

Function storageDidPerformUpdate Batch update error

Got this little ditty from the compiler as it threw an runtime exception

Invalid update: invalid number of items in section 0. The number of items contained in an existing section after the update (10) must be equal to the number of items contained in that section before the update (10), plus or minus the number of items inserted or deleted from that section (10 inserted, 0 deleted) and plus or minus the number of items moved into or out of that section (0 moved in, 0 moved out).

Now i recall a year or so back running into the same problem when working with UICollectionViews and i did come up with a solution but it escapes me, luckily i believe someone else documented their struggles and solutions on the same problem.

any change of an update or would you like me to make a pull request? I wont be able to do so myself for a few days at least...Work deadlines and such.

startManagingWithDelegate crash

class: DTCollectionViewManager.swift
method: open func startManaging(withDelegate delegate : DTCollectionViewManageable)
line 204: guard let collectionView = delegate.collectionView ?? delegate.optionalCollectionView else {
preconditionFailure("Call startManagingWithDelegate: method only when UICollectionView has been created")
}

DTCollectionViewManager/DTCollectionViewManager.swift:204: Fatal error: Call startManagingWithDelegate: method only when UICollectionView has been created
2021-07-04 19:12:51.399824+0200 Doctorate[2073:95370] DTCollectionViewManager/DTCollectionViewManager.swift:204: Fatal error: Call startManagingWithDelegate: method only when UICollectionView has been created

Separate classes?

Hello, I really like your extension, it's awesome!
But, do you think about separate controller and UIViewController classes. I think It will be better to encapsulate UICollectionView logic inside a NSObject-controller, and add it's as a property to your UIViewController. Because for example I use separate logic for views and controllers, using MVVM pattern, and this realisation sometimes is painfully for my architecture.
What do you think about this?

collectionview optional?

Hello! Why is collection view in DTCollectionViewManageable optional? It will be better if it is nonoptional value like in DTTableViewManageable

public protocol DTTableViewManageable : class
{
    /// Table view, that will be managed by DTTableViewManager
    var tableView : UITableView! { get }
}
public protocol DTCollectionViewManageable : class
{
    /// Collection view, that will be managed by DTCollectionViewManager
    var collectionView : UICollectionView? { get }
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.