Swiftpack.co - Package - DenTelezhkin/DTCollectionViewManager

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

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

CocoaPods:

pod 'DTCollectionViewManager', '~> 8.0.0'

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.

Github

link
Stars: 296

Dependencies

Used By

Total: 0

Releases

8.0.0 -

Added

  • Registering events for UICollectionViewDelegateFlowLayout protocol now triggers an anomaly, if different layout class is used (for example UICollectionViewCompositionalLayout)

8.0.0-beta.1 -

This is a major release with some breaking changes, please read DTCollectionViewManager 8.0 Migration Guide

New

  • Cell and supplementary view events are now available inside mapping closure directly, for example:
// Previous releases
manager.register(PostCell.self)
manager.didSelect(PostCell.self) { cell, model, indexPath in
    // React to selection
}

// New
manager.register(PostCell.self) { mapping in
    mapping.didSelect { cell, model, indexPath in

    }
}

Those events are now tied to ViewModelMapping instance, which means, that events, registered this way, will only trigger, if mapping condition of current mapping applies. For example:

manager.register(PostCell.self) { mapping in
    mapping.condition = .section(0)
    mapping.didSelect { cell, model, indexPath in  
        // This closure will only get called, when user selects cell in the first section
    }
}
manager.register(PostCell.self) { mapping in
    mapping.condition = .section(1)
    mapping.didSelect { cell, model, indexPath in  
        // This closure will only get called, when user selects cell in the second section
    }
}
  • It's now possible to register collection view cells, that don't conform to DTModelTransfer protocol:
manager.register(UICollectionViewCell.self, String.self) { cell, indexPath, model in
    // configure cell with model which is of type String when passed into configuration closure.
}

This is particularly useful on iOS / tvOS 14 and higher, where you can configure UICollectionViewListCell without needing to subclass it. Cells, registered in this way, can safely coexist with cells, that conform to DTModelTransfer protocol. Conditional mappings are also supported (multiple trailing closures syntax available in Swift 5.3):

manager.register(UICollectionViewCell.self, for: String.self) {
    $0.condition = .section(0)
} handler { cell, indexPath, model in
  // configure cell with model which is of type String when passed into configuration closure.
}
  • Added event reaction for UICollectionViewDelegate.collectionView(_:canEditItemAt:) delegate method.
  • Added event reactions for tvOS 13 TVCollectionViewDelegateFullScreenLayout protocol from TVUIKit framework.
  • New readme and in-depth documentation, split into several sections for developer convenience.

Changed

  • On iOS/tvOS 14 and higher, cell and supplementary views now use UICollectionView.dequeueConfiguredReusableCell and UICollectionView.dequeueConfiguredReusableSupplementary to be dequeued.
  • DTModelTransfer update(with:) method for such cells and supplementary views is called immediately after dequeueConfiguredReusableCell \ dequeueConfiguredReusableSupplementary return.
  • Generic placeholders for cell/model/view methods have been improved for better readability.

Breaking

This release requires Swift 5.3. Minimum iOS / tvOS deployment targets are unchanged (iOS 11, tvOS 11).

Some context: this release heavily relies on where clauses on contextually generic declarations, that are only available in Swift 5.3 - SE-0267.

  • Cells, headers and footers created in storyboard now need to be explicitly configured in view mapping:
register(StoryboardCell.self) { mapping in
    mapping.cellRegisteredByStoryboard = true
}

registerHeader(StoryboardHeader.self) { mapping in
    mapping.supplementaryRegisteredByStoryboard = true
}
  • All non-deprecated registration methods now have an additional handler closure, that allows to configure cells/headers/footers/supplementary views that are dequeued from UICollectionView. This is a direct replacement for configure(_:_:, configureHeader(_:_:), configureFooter(_:_:) and configureSupplementary(_:ofKind:_:, that are all now deprecated.
  • On iOS / tvOS 14 / Xcode 12 and higher handler closure, that is passed to registration methods, is used to call new dequeueConfiguredReusableCell(using:for:item:) and dequeueConfiguredReusableSupplementary(using:for:) methods on UICollectionView. Please note, that handler closure is called before DTModelTransfer.update(with:) method because of how new UICollectionView dequeue API works.
  • ViewModelMapping is now a generic class, that captures view and model information(ViewModelMapping<T,U>).
  • CollectionViewUpdater.batchUpdatesInProgress property was removed.

Deprecated

  • Several cell/header/footer/supplementary view registration methods have been deprecated to unify registration logic. Please use register(_:mapping:handler:), registerHeader(_:mapping:handler:), registerFooter(_:mapping:handler:) and registerSupplementary(_:forKind:mapping:handler:) as a replacements for all of those methods. For more information on those changes, please read migration guide
  • DTCollectionViewManager.configureEvents(for:_:), it's functionality has become unnecessary since mapping closure of cell/supplementary registration now captures both cell and model type information for such events.
  • DTCollectionViewManager.configureDiffableDataSource(modelProvider:) for non-hashable data models. Please use configureDiffableDataSource method for models, that are Hashable. From Apple's documentation: If you’re working in a Swift codebase, always use UICollectionViewDiffableDataSource instead.

Fixed

  • Supplementary views now correctly use ViewModelMapping.reuseIdentifier instead of falling back to name of the view class.
  • Several event API's have been improved to allow returning nil for methods, that accept nil as a valid value: contextMenuConfiguration, previewForHighlightingContextMenu, previewForDismissingContextMenu

7.2.0 -

Changed

  • Deployment targets - iOS 11 / tvOS 11.
  • Minimum Swift version required: 5.0
  • Added support for DTModelStorage/Realm with Realm 5

Please note, that this framework version source is identical to previous version, which supports iOS 8 / tvOS 9 / Swift 4.2 and higher.

7.1.0 -

Changed

  • It's not longer necessary to import DTModelStorage framework to use it's API's. import DTCollectionViewManager now implicitly exports DTModelStorage as well.

7.0.0 -

  • willCommitMenuWithAnimator method has been made unavailable for Xcode 11.2, because UICollectionViewDelegate method it used has been removed from UIKit on Xcode 11.2.

7.0.0-beta.2 -

  • Added support for Xcode versions, that are older than Xcode 11.

7.0.0-beta.1 -

This is a major release with some breaking changes, please read DTCollectionViewManager 7.0 Migration Guide

Changed

  • DTCollectionViewManager now requires to be built with Swift 4.2 and later.
  • Anomaly event verification now allows subclasses to prevent false-positives.
  • animateChangesOffScreen property on CollectionViewUpdater that allows to turn off animated updates for UICollectionView when it is not on screen.

Added

  • configureDiffableDataSource(modelProvider:) method to enable UICollectionViewDiffableDataSource with DTCollectionViewManager.
  • DTCollectionViewManager.supplementaryStorage getter, that conditionally casts current storage to SupplementaryStorage protocol.
  • Ability to customize bundle, from which xib files are loaded from by setting bundle property on ViewModelMapping in mappingBlock. As before, bundle defaults to Bundle(for: ViewClass.self).

New method wrappers for iOS 13 API

  • shouldBeginMultipleSelectionInteraction
  • didBeginMultipleSelectionInteraction
  • didEndMultipleSelectionInteraction
  • contextMenuConfiguration(for:)
  • previewForHighlightingContextMenu
  • previewForDismissingContextMenu
  • willCommitMenuWithAnimator

Removed

  • Usage of previously deprecated and now removed from DTModelStorage ViewModelMappingCustomizing protocol.

Breaking

DTModelStorage header, footer and supplementary model handling has been largely restructured to be a single closure-based API. Read more about changes in DTModelStorage changelog. As a result of those changes, several breaking changes in DTCollectionViewManager include:

  • SectionModel extension with collectionHeaderModel and collectionFooterModel properties has been removed.
  • Because headers/footers are now a closure based API, setSectionHeaderModels and setSectionFooterModels do not create sections by default, and do not call collectionView.reloadData.

Other breaking changes:

  • collectionViewUpdater will contain nil if DTCollectionViewManager is configured to work with UICollectionViewDiffableDataSource.
  • DTCollectionViewNonOptionalManageable protocol was removed and replaced by collectionView property on DTCollectionViewManageable protocol. One of collectionView/optionalCollectionView properties must be implemented by DTCollectionViewManageable instance to work with DTCollectionViewManager.
  • collectionView property in DTCollectionViewManageable protocol is now ImplicitlyUnwrappedOptional instead of Optional. This change is done to unify API with UICollectionViewController change and DTTableViewManager API for consistency.

WARNING Because of default implementations for new property this will not show as a compile error, instead crashing in runtime. Please make sure to update all definitions of

var collectionView: UICollectionView?

to

var collectionView: UICollectionView!.

If you need optional collection view, use optionalCollectionView property instead.

Deprecated

Following methods have been deprecated due to their delegate methods being deprecated in iOS 13:

  • shouldShowMenuForItemAt
  • canPerformAction
  • performAction

6.6.0 -

  • Added support for Swift Package Manager in Xcode 11

6.5.0 -

Added

  • Convenience constructor for DTCollectionViewManager object: init(storage:) that allows to create it's instance without initializing MemoryStorage.
  • Static variable defaultStorage on DTCollectionViewManager that allows to configure which Storage class is used by default.
  • Documentation
  • Support for Xcode 10.2 and Swift 5

Removed

  • Support for Xcode 9 and Swift 3

6.4.2 -

  • move(:_,:_) method was deprecated and no longer works due to a logic bug, that can prevent this method from being called if sourceIndexPath is off screen when this event was called by UICollectionView. Please use new method moveItemAtTo(:_) to subscribe to move events in the datasource.

6.4.1 -

  • Fix infinite recursion bug with UICollectionView.canFocusItemAt(:) method(thanks, @skydivedan!)

6.4.0 -

  • Support for Xcode 10 and Swift 4.2

6.3.0 -

Added

  • Anomaly-detecting and reporting system for DTCollectionViewManager. Read more about it in Anomaly Handler Readme section. Anomaly handler system requires Swift 4.1 and higher.
  • Support for Swift 4.2 in Xcode 10 (beta 1).

Changed

  • Calling startManaging(withDelegate:_) method is no longer required.

Breaking

  • viewFactoryErrorHandler property on DTCollectionViewManager was removed, all supported errors and warnings are now a part of anomaly reporting system

6.1.1 -

6.1.0 -

6.1.0-beta.1 -

  • Implemented new system for deferring datasource updates until performBatchUpdates block. This system is intended to fight crash, that might happen when performBatchUpdates method is called after UITableView.reloadData method(for example after calling memoryStorage.setItems, and then immediately memoryStorage.addItems). This issue is detailed in https://github.com/DenHeadless/DTCollectionViewManager/issues/27 and https://github.com/DenHeadless/DTCollectionViewManager/issues/23. This crash can also happen, if iOS 11 API UITableView.performBatchUpdates is used. This system is turned on by default. If, for some reason, you want to disable it and have old behavior, call:
manager.memoryStorage.defersDatasourceUpdates = false

Please note, though, that new default behavior is recommended, because it is more stable and works the same on both UITableView and UICollectionView.

  • collectionViewUpdater property on DTCollectionViewManager is now of CollectionViewUpdater type instead of opaque StorageUpdating type. This should ease use of this object and prevent type unneccessary type casts.

6.0.0 -

  • Updated for Xcode 9.1 / Swift 4.0.2

6.0.0-beta.3 -

  • Makes DTCollectionViewManager property weak instead of unowned to prevent iOS 10-specific memory issues/crashes.

6.0.0-beta.2 -

  • Build with Xcode 9.0 final release.
  • Fixed partial-availability warnings.

6.0.0-beta.1 -

This is a major release with some breaking changes, please read DTTableViewManager 6.0 Migration Guide

  • Added updateVisibleCells(_:) method, that allows updating cell data for visible cells with callback on each cell. This is more efficient than calling reloadData when number of elements in UICollectionView does not change, and only contents of items change.
  • Implement configureEvents(for:_:) method, that allows batching in several cell events to avoid using T.ModelType for events, that do not have cell created.
  • Added DTCollectionViewDropPlaceholderContext wrapper with convenience support for UICollectionView placeholders.
  • Implemented UICollectionViewDragDelegate and UICollectionViewDropDelegate events.
  • Added 10 more UICollectionViewDelegate and UICollectionViewDelegateFlowLayout events.
  • Added missing events for UICollectionViewDatasource protocol: collectionView:moveItemAt:to:, indexTitlesFor:, collectionView:indexPathForIndexTitle:at:
  • Implemented conditional mappings
  • UICollectionViewDelegate and UICollectionViewDatasource implementations have been refactored from DTCollectionViewManager to DTCollectionViewDelegate and DTCollectionViewDataSource classes.
  • Added DTCollectionViewNonOptionalManageable protocol, that can be used with non-optional UICollectionView properties on your managed instance.

5.3.1 -

  • Initial support for Swift 3.2 (Xcode 9 beta-1).
  • Fixed registerNiblessHeader and registerNiblessFooter to properly call nibless supplementary methods.

5.3.0 -

  • Use new events system from DTModelStorage, that allows events to be properly called for cells, that are created using ViewModelMappingCustomizing protocol.

5.2.0 -

  • Setting CollectionViewUpdater instance to collectionViewUpdater property on DTCollectionViewManager now triggers didUpdateContent closure on CollectionViewUpdater.

5.1.0 -

Dependency changelog -> DTModelStorage 4.0.0 and higher

  • CollectionViewUpdater has been rewritten to use new StorageUpdate properties that track changes in order of their occurence.
  • CollectionViewUpdater reloadItemClosure and DTCollectionViewManager updateCellClosure now accept indexPath and model instead of just indexPath. This is done because update may happen after insertions and deletions and object that needs to be updated may exist on different indexPath.

5.0.0 -

No changes

5.0.0-beta.3 -

  • DTModelStorage/Realm dependency now requires Realm 2.0

5.0.0-beta.2 -

  • Enables RealmStorage from DTModelStorage dependency.

5.0.0-beta.1 -

This is a major release, written in Swift 3. Read [Migration guide](Documentation/DTCollectionViewManager 5 migration guide.md) with descriptions of all features and changes.

Dependency changelog -> DTModelStorage 3.0.0 and higher

Added

  • New events system that covers almost all available UICollectionViewDelegate, UICollectionViewDataSource and UICollectionViewDelegateFlowLayout delegate methods.
  • New class - CollectionViewUpdater, that is calling all animation methods for UICollectionView when required by underlying storage.
  • updateCellClosure method on DTCollectionViewManager, that manually updates visible cell instead of calling collectionView.reloadItemsAt(_:) method.
  • coreDataUpdater property on DTCollectionViewManager, that creates CollectionViewUpdater object, that follows Apple's guide for updating UICollectionView from NSFetchedResultsControllerDelegate events.
  • isManagingCollectionView property on DTCollectionViewManager.
  • unregisterCellClass(_:), unregisterHeaderClass(_:), unregisterFooterClass(_:), unregisterSupplementaryClass(_:forKind:) methods to unregister mappings from DTCollectionViewManager and UICollectionView

Changed

  • Swift 3 API Design guidelines have been applied to all public API.
  • Event system is migrated to new EventReaction class from DTModelStorage
  • Now all view registration methods use NSBundle(forClass:) constructor, instead of falling back on DTCollectionViewManager viewBundle property. This allows having cells from separate bundles or frameworks to be used with single DTCollectionViewManager instance.

Removals

  • viewBundle property on DTCollectionViewManager
  • itemForVisibleCell, itemForCellClass:atIndexPath:, itemForHeaderClass:atSectionIndex:, itemForFooterClass:atSectionIndex: were removed - they were not particularly useful and can be replaced with much shorter Swift conditional typecasts.
  • All events methods with method pointer semantics. Please use block based methods instead.
  • registerCellClass:whenSelected method, that was tightly coupling something that did not need coupling.

4.8.0 -

Changed

  • Now all view registration methods use NSBundle(forClass:) constructor, instead of falling back on DTCollectionViewManager viewBundle property. This allows having cells from separate bundles or frameworks to be used with single DTCollectionViewManager instance.

Deprecations

  • viewBundle property on DTCollectionViewManager

4.7.0 -

Dependency changelog -> DTModelStorage 2.6.0 and higher