Swift: Generic Reusable collection view trying to adding multiple cell support - swift

I followed several cars to create a collectionViewDataSource, and I am extremely happy with the result:
let dataSource = CollectionViewDataSourceProvider(items: domains, cell: EditDomainsCollectionViewCell.self) { indexPath, item, cell in
cell.setup(with: item)
}.registerCell(for: domainsCollectionView)
self.datasource = dataSource
domainsCollectionView.dataSource = datasource
domainsCollectionView.delegate = datasource
Here my dataSourceProvider class:
class CollectionViewDataSourceProvider<Item, Cell: UICollectionViewCell & NibLoadableView>: NSObject, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
typealias CellConfigurator = (_ indexPath: IndexPath, _ item: Item, _ cell: Cell) -> ()
typealias SizeConfigurator = (_ indexPath: IndexPath, _ item: Item) -> CGSize
typealias WidthConfigurator = (_ section: Int) -> CGFloat
typealias InsetConfigurator = (_ section: Int) -> UIEdgeInsets
typealias SelectConfigurator = (_ indexPath: IndexPath, _ item: Item) -> ()
private let items: [Item]
private let cell: Cell.Type
private let cellConfigurator: CellConfigurator
private var sizeConfigurator: SizeConfigurator?
private var minimumLineSpacingForSectionAtConfigurator: WidthConfigurator?
private var minimumInteritemSpacingForSectionAt: WidthConfigurator?
private var insetForSectionAt: InsetConfigurator?
private var didSelectItemAt: SelectConfigurator?
init(items: [Item], cell: Cell.Type, cellConfigurator: #escaping CellConfigurator) {
self.items = items
self.cell = cell
self.cellConfigurator = cellConfigurator
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return items.count
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let model = items[indexPath.row]
let cell = loadNIB()
return sizeConfigurator?(indexPath, model) ?? cell.frame.size
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return minimumInteritemSpacingForSectionAt?(section) ?? (collectionView.collectionViewLayout as? UICollectionViewFlowLayout)?.minimumLineSpacing ?? 1
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return insetForSectionAt?(section) ?? (collectionView.collectionViewLayout as? UICollectionViewFlowLayout)?.sectionInset ?? UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
didSelectItemAt?(indexPath, items[indexPath.row])
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return minimumLineSpacingForSectionAtConfigurator?(section) ?? (collectionView.collectionViewLayout as? UICollectionViewFlowLayout)?.minimumLineSpacing ?? 1
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let model = items[indexPath.row]
let cell: Cell = collectionView.dequeueReusableCell(forIndexPath: indexPath)
cellConfigurator(indexPath, model, cell)
return cell
}
private func loadNIB() -> Cell {
return Bundle(for: Cell.self as AnyClass).loadNibNamed(String(describing: Cell.self), owner: nil, options: nil)![0] as! Cell
}
}
extension CollectionViewDataSourceProvider {
func registerCell(for collectionView: UICollectionView, flowLayout: UICollectionViewFlowLayout? = nil) -> CollectionViewDataSourceProvider {
collectionView.register(cell)
if let flowLayout = flowLayout {
collectionView.setCollectionViewLayout(flowLayout, animated: true)
}
return self
}
func heightForRow(config: #escaping SizeConfigurator) -> CollectionViewDataSourceProvider {
sizeConfigurator = config
return self
}
func minimumLineSpacingForSectionAt(config: #escaping WidthConfigurator) -> CollectionViewDataSourceProvider {
minimumLineSpacingForSectionAtConfigurator = config
return self
}
func minimumInteritemSpacingForSectionAt(config: #escaping WidthConfigurator) -> CollectionViewDataSourceProvider {
minimumInteritemSpacingForSectionAt = config
return self
}
func insetForSectionAt(config: #escaping InsetConfigurator) -> CollectionViewDataSourceProvider {
insetForSectionAt = config
return self
}
func didSelectedAt(config: #escaping SelectConfigurator) -> CollectionViewDataSourceProvider {
didSelectItemAt = config
return self
}
}
After all, I am trying to implement the possibility of using several different cell by protocol but I cannot adapt it with my provider class.
Here is the pattern I want to make to create multiple cell types quite simply:
enum CellType {
case typeOne, typeTwo
var identifier: String {
switch self {
case .typeOne:
return NSStringFromClass(CellOne.self)
case .typeTwo:
return NSStringFromClass(CellTwo.self)
}
}
}
protocol CustomElement {
var type: CellType { get }
}
class MyFirstObject: CustomElement {
var type: CellType { .typeOne}
}
class MySecondObject: CustomElement {
var type: CellType { .typeTwo}
}
protocol CellElement where Self: UICollectionViewCell & NibLoadableView {
func configure(with object: CustomElement)
}
class CellOne: UICollectionViewCell, CellElement, NibLoadableView {
func configure(with object: CustomElement) {
guard let object = object as? MySecondObject else { return }
//
}
}
class CellTwo: UICollectionViewCell, CellElement, NibLoadableView {
func configure(with object: CustomElement) {
guard let object = object as? MyFirstObject else { return }
//
}
}
The NibLoadableView protocol comes from this gist, to save cells easily https://gist.github.com/gonzalezreal/92507b53d2b1e267d49a
I want my collectionViewDataSourceProvider to be able to receive in the init an array of UICollectionView cell, and if there is more than one cell then implement the protocol logic for multiple cell. But I can't do it.
For example, I am trying to set the protocol as a generic parameter in my datasource, but it tells me that the protocol does not inherit from IUCollectionView.
I have no direction how to do this thank you for your help

Related

index out of range double array collection view

I fill cellTypes array
func fillCellTypes() {
cellTypes = []
cellTypes.append(Array(repeating: CellType.vanc, count: self.vancPhotos.count))
cellTypes.append(Array(repeating: CellType.calg, count: self.calgPhotos.count))
cellTypes.append(Array(repeating: CellType.mon, count: self.monPhotos.count))
cellTypes.append(Array(repeating: CellType.ott, count: self.ottPhotos.count))
cellTypes.append(Array(repeating: CellType.tor, count: self.torPhotos.count))
self.collectionView.reloadData()
print(cellTypes)
}
My CellType enum
enum CellType {
case vanc
case calg
case ott
case tor
case mon
}
var cellTypes: [[CellType]] = []
in config I fill all photos arrays
var vancPhotos: [PhotoModel] = []
var calgPhotos: [PhotoModel] = []
var ottPhotos: [PhotoModel] = []
var torPhotos: [PhotoModel] = []
var monPhotos: [PhotoModel] = []
var photos: [PhotoModel] = []
func configCollectionView() {
photoViewModel.getPhotos()
photos = photoViewModel.photos
vancPhotos = photoViewModel.vancPhotos
calgPhotos = photoViewModel.calgPhotos
ottPhotos = photoViewModel.ottPhotos
torPhotos = photoViewModel.torPhotos
monPhotos = photoViewModel.monPhotos
Inside cellForItemAt I get index out of range at switch.
extension WeatherDetailVC: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return cellTypes.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
switch cellTypes[indexPath.section][indexPath.row] {
case .tor: return configureTorontoCell(indexPath: indexPath)
...
}
}
func configureTorontoCell(indexPath: IndexPath) -> PhotoCollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: PhotoCollectionViewCell.identifier, for: indexPath) as? PhotoCollectionViewCell
cell?.setup(photoViewModel.torPhotos[indexPath.row])
return cell!
}
return cellTypes.count is 3 so my arrays are filled but cellforitem is some how wrong.
You would need to implement this differently you need multiple sections as your data consists of an array of arrays:
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
cellTypes[section].count
}
and add this function:
func numberOfSections(in collectionView: UICollectionView) -> Int {
cellTypes.count
}

Cant display image in UICollectionView cell

I need to display images in collection view cells but when I'm trying to do that I'm getting 10 empty cells and I don't know where im making mistakes
Here is my code of ViewController
class NewGalleryViewController: UIViewController {
var presenter: ViewToPresenterPhotoProtocol?
var builder: GalleryRequestBuilder?
#IBOutlet var collectionView: UICollectionView!
let reuseIdentifier = "customCVCell"
#objc func refresh() {
presenter?.refresh()
}
override func viewDidLoad() {
super.viewDidLoad()
self.setupPresenterIfNeed()
presenter?.viewDidLoad()
// Do any additional setup after loading the view.
}
func setupPresenterIfNeed() {
self.collectionView.backgroundColor = UIColor.white
if self.presenter == nil {
let presenter = GalleryPresenter()
presenter.view = self
self.presenter = presenter
self.builder = GalleryRequestBuilder()
}
}
}
extension NewGalleryViewController: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.presenter?.photos.count ?? 0
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! PhotoCollectionViewCell
KFImage.url(builder?.createImageUrl(name: (presenter?.photos[indexPath.item].name)!))
.onSuccess { result in
cell.imageView.image = result.image
}
return cell
}
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: 180, height: 128)
}
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 1.0
}
func collectionView(_ collectionView: UICollectionView, layout
collectionViewLayout: UICollectionViewLayout,
minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 20.0
}
// MARK: - UICollectionViewDelegate protocol
private func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
// handle tap events
print("You selected cell #\(indexPath.item)!")
}
}
extension NewGalleryViewController: PresenterToViewPhotoProtocol{
func onFetchPhotoSuccess() {
self.collectionView.reloadData()
self.collectionView!.collectionViewLayout.invalidateLayout()
self.collectionView!.layoutSubviews()
self.collectionView.refreshControl?.endRefreshing()
}
func onFetchPhotoFailure(error: String) {
print("View receives the response from Presenter with error: \(error)")
self.collectionView.refreshControl?.endRefreshing()
}
}
And Here is the code of cell
class PhotoCollectionViewCell: UICollectionViewCell {
#IBOutlet weak var imageView: UIImageView!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
}
I've checked the link I'm making request to and it works. So problem is not in link. Maybe I should reload items after getting images?
You should set your UICollectionView delegate and data source once the view is loaded:
override func viewDidLoad() {
super.viewDidLoad()
// Add this lines
collectionView.delegate = self
collectionView.dataSource = self
self.setupPresenterIfNeed()
presenter?.viewDidLoad()
}

Proxy calls to UICollectionView in Swift. Being able to pass Proxy as a method argument

Code:
class ColletionProxy: UICollectionView {
weak var collectionView: UICollectionView?
init(collectionView: UICollectionView) {
super.init(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout())
self.collectionView = collectionView
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func dequeueReusableCell(withReuseIdentifier identifier: String, for indexPath: IndexPath) -> UICollectionViewCell {
return collectionView!.dequeueReusableCell(withReuseIdentifier: identifier, for: indexPath)
}
}
Usage:
let dataSource: UICollectionViewDataSource = MyCustomCollectionViewDataSource()
let realCollectionView = UICollectionView()
let proxy = CollectionProxy(realCollectionView)
let SUPERCELL = dataSource.collectionView(proxy, cellForItem at indexPath)
I want to make ColletionProxy not to be a subclass of the UICollectionView while being able to use it the way shown in Usage
To be precise, I would like to have a set of methods in the Proxy class mimicking the methods of the UICollectionView to be able to drop in the Proxy class instead of the UICollectionView when necessary.
Code I've finished with:
Proxy:
#import "CollectionViewProxy.h"
#interface CollectionViewProxy()
#property (weak, nonatomic) UICollectionView *collectionView;
#end
#implementation CollectionViewProxy
- (instancetype)initWithCollectionView:(UICollectionView *)collectionView {
if (self) {
self.collectionView = collectionView;
}
return self;
}
- (__kindof UICollectionViewCell *)dequeueReusableCellWithReuseIdentifier:(NSString *)identifier forIndexPath:(NSIndexPath *)indexPath {
UICollectionViewCell *cell = [_collectionView dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:indexPath];
return cell;
}
- (BOOL)isKindOfClass:(Class)aClass {
if (aClass == [UICollectionView class]) {
return YES;
}
return [super isKindOfClass:aClass];
}
#end
ComposedDataSource:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let ds = dataSources.first!
// let ds = GET THE RELEVANT DATASOURCE HERE
let collectionView: UICollectionView = ((CollectionViewProxy(collectionView: collectionView) as Any) as! UICollectionView) // Any way to avoid this cast?
return ds.collectionView(collectionView, cellForItemAt: indexPath)
}
Is there any way to avoid this cast through Any?
let collectionView: UICollectionView = ((CollectionViewProxy(collectionView: collectionView) as Any) as! UICollectionView)
Even better approach, no need to do Objective-C<>Swift bridging:
import UIKit
protocol CollectionViewProtocol {
func dequeueReusableCell(withReuseIdentifier identifier: String, for indexPath: IndexPath) -> UICollectionViewCell
}
extension UICollectionView: CollectionViewProtocol {}
protocol ComposableCollectionDataSource: UICollectionViewDataSource {
func collectionView(_ collectionView: CollectionViewProtocol, numberOfItemsInSection section: Int) -> Int
func collectionView(_ collectionView: CollectionViewProtocol, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
}
class MyDataSource: NSObject, ComposableCollectionDataSource {
// MARK: Real implementation can be used with any class conforming to CollectionViewProtocol
func collectionView(_ collectionView: CollectionViewProtocol, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
return collectionView.dequeueReusableCell(withReuseIdentifier: "test", for: indexPath)
}
func collectionView(_ collectionView: CollectionViewProtocol, numberOfItemsInSection section: Int) -> Int {
return 1
}
// MARK: Methods "Mirroring" UICollectionViewDataSource
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.collectionView(collectionView, numberOfItemsInSection: section)
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
return self.collectionView(collectionView, cellForItemAt: indexPath)
}
}
class Proxy: CollectionViewProtocol {
private let hiddenCollectionView = UICollectionView()
func dequeueReusableCell(withReuseIdentifier identifier: String, for indexPath: IndexPath) -> UICollectionViewCell {
// Transform IndexPath Here
let transformedIndexPath = indexPath
return hiddenCollectionView.dequeueReusableCell(withReuseIdentifier: identifier, for: transformedIndexPath)
}
}
let dataSource = MyDataSource()
let realCollectionView = UICollectionView()
let fakeCollectionView = Proxy()
let cell = dataSource.collectionView(realCollectionView, cellForItemAt: IndexPath())
let anotherCell = dataSource.collectionView(fakeCollectionView, cellForItemAt: IndexPath())

Using IGListKit for horizontal list with paging

I am building an app that relies heavily on collection view so I decided to use IGListKit. Here is what I have to do:
and here is what I have:
... and it is not moving in any direction!
Here is my code:
import UIKit
import IGListKit
class MatchCollectionViewCell: UICollectionViewCell {
var helloWorld = "Hello World"
}
class LabelSectionController: ListSectionController {
override func sizeForItem(at index: Int) -> CGSize {
return CGSize(width: collectionContext!.containerSize.width, height: 55)
}
override func cellForItem(at index: Int) -> UICollectionViewCell {
return collectionContext!.dequeueReusableCell(of: MatchCollectionViewCell.self, for: self, at: index)
}
}
class MatchViewController: UIViewController {
// MARK: - Outlets
#IBOutlet weak var matchCollectionView: UICollectionView!
// MARK: - Variables
let layout = UICollectionViewFlowLayout()
lazy var adapter: ListAdapter = {
return ListAdapter(updater: ListAdapterUpdater(), viewController: self, workingRangeSize: 0)
}()
var users: [User] = []
// MARK: - Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
matchCollectionView.dataSource = self
matchCollectionView.delegate = self
matchCollectionView.collectionViewLayout = CenterCellCollectionViewFlowLayout()
matchCollectionView.isPagingEnabled = true
layout.minimumLineSpacing = 8
layout.scrollDirection = .horizontal
for i in 1...10 {
let newMatchingPreferences = MatchingPreferences(preferedAge: (23, 33))
let newUser = User(id: i, name: "Some Name \(i)", email: "some_name#gmail.com", age: 27, location: "New York", isOnboarded: true, isPremium: true, matchingPreferences: newMatchingPreferences)
users.append(newUser)
}
}
// MARK: - Actions
// MARK: - Methods
}
extension MatchViewController: ListAdapterDataSource {
func objects(for listAdapter: ListAdapter) -> [ListDiffable] {
return self.users
}
func listAdapter(_ listAdapter: ListAdapter, sectionControllerFor object: Any) -> ListSectionController {
return LabelSectionController()
}
func emptyView(for listAdapter: ListAdapter) -> UIView? {
let view = UIView()
view.backgroundColor = .lightGray
return view
}
}
extension MatchViewController: UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 10
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = matchCollectionView.dequeueReusableCell(withReuseIdentifier: "MatchCell", for: indexPath) as! MatchCollectionViewCell
print(cell.helloWorld)
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: view.bounds.width - 32, height: view.bounds.height - 40)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return .zero
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 0
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 0
}
}
class CenterCellCollectionViewFlowLayout: UICollectionViewFlowLayout {
var mostRecentOffset: CGPoint = CGPoint.zero
override func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint {
if velocity.x == 0 {
return self.mostRecentOffset
}
guard let cv = self.collectionView,
let attributesForVisibleCells = self.layoutAttributesForElements(in: cv.bounds) else {
// Fallback
self.mostRecentOffset = super.targetContentOffset(forProposedContentOffset: proposedContentOffset)
return self.mostRecentOffset
}
let halfWidth = cv.bounds.size.width * 0.5
var candidateAttributes: UICollectionViewLayoutAttributes?
for attributes in attributesForVisibleCells {
// Skip comparison with non-cell items (headers and footers)
if attributes.representedElementCategory != UICollectionElementCategory.cell {
continue
}
if (attributes.center.x == 0) || (attributes.center.x > (cv.contentOffset.x + halfWidth) && velocity.x < 0) {
continue
}
candidateAttributes = attributes
}
// Beautification step , I don't know why it works!
if proposedContentOffset.x == -(cv.contentInset.left) {
return proposedContentOffset
}
guard let attributes = candidateAttributes else {
return mostRecentOffset
}
self.mostRecentOffset = CGPoint(x: floor(attributes.center.x - halfWidth), y: proposedContentOffset.y)
return self.mostRecentOffset
}
}
Basically, I know how to make crude collection view but paging and centering is a bit hard for me. I feel like I am close but obviously something is missing. I hope somebody can point me to a right direction! Thanks.
extension ViewController: UIScrollViewDelegate {
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
if scrollView == self.collectionView {
var currentCellOffset = self.collectionView.contentOffset
currentCellOffset.x += self.collectionView.frame.width / 2
if let indexPath = self.collectionView.indexPathForItem(at: currentCellOffset) {
self.collectionView.scrollToItem(at: indexPath, at: .centeredHorizontally, animated: true)
}
}
}
}
make sure to set the
adapter.scrollDelegate = self
and
collectionView.decelerationRate = UIScrollViewDecelerationRateFast
(for more of a native iOS feel)
this worked for me I hope it helps someone else!

Swift OSX - Rearrange NSCollectionView with drag and drop not working

I am trying to rearrange the item inside a NSCollectionView but it's not working. It doesn't call some delegate methods as validate drop and accept drop. It calls func collectionView(_ collectionView: NSCollectionView, pasteboardWriterForItemAt indexPath: IndexPath) -> NSPasteboardWriting? or func collectionView(_ collectionView: NSCollectionView, writeItemsAt indexPaths: Set<IndexPath>, to pasteboard: NSPasteboard) -> Bool, but after that is doesn't call the other methods.
I think the problem is that I am bot being able to register the correct types for the drag and drop because when I move the items inside the collection view it doesn't show a place where the items can be dropped and after I drop it the items bounce back to their original places.
Here is the code:
FotoProdutoLojaCollectionViewItem.swift
import Cocoa
class FotoProdutoLojaCollectionViewItem: NSCollectionViewItem {
#IBOutlet weak var fotoProdutoLojaImageView: NSImageView!
#IBOutlet weak var fotoCapaImageView: NSImageView!
override func viewDidLoad() {
super.viewDidLoad()
fotoCapaImageView.isHidden = true
}
}
The items of the CollectionView
func collectionView(_ collectionView: NSCollectionView, itemForRepresentedObjectAt indexPath: IndexPath) -> NSCollectionViewItem {
var item = NSCollectionViewItem()
item = collectionView.makeItem(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "FotoProdutoLojaCollectionViewItem"), for: indexPath)
let fotosProdutoLojaCollectionViewItem = item as! FotoProdutoLojaCollectionViewItem
produtoLoja?.fotos[indexPath.item].foto?.getDataInBackground(block: {
(data: Data?, error: Error?) -> Void in
if error == nil {
fotosProdutoLojaCollectionViewItem.fotoProdutoLojaImageView.image = NSImage(data: data!)
}
})
if produtoLoja!.fotos[indexPath.item].imagemCapa {
fotosProdutoLojaCollectionViewItem.fotoCapaImageView.isHidden = false
}else {
fotosProdutoLojaCollectionViewItem.fotoCapaImageView.isHidden = true
}
return item
}
override func viewDidLoad() {
super.viewDidLoad()
fotosProdutoLojaCollectionView.delegate = self
fotosProdutoLojaCollectionView.dataSource = self
fotosProdutoLojaCollectionView.registerForDraggedTypes([NSPasteboard.PasteboardType(kUTTypeData as String)])
fotosProdutoLojaCollectionView.setDraggingSourceOperationMask(.move, forLocal: true)
}
Here is the pasteboardWriterForItemAt indexPath. I have tried all the commented lines.
func collectionView(_ collectionView: NSCollectionView, pasteboardWriterForItemAt indexPath: IndexPath) -> NSPasteboardWriting? {
let pb = NSPasteboardItem()
var data: Data?
do {
try data = produtoLoja?.fotos[indexPath.item].foto?.getData()
} catch {
}
pb.setData(data!, forType: NSPasteboard.PasteboardType.string)
return pb
//return NSPasteboardItem()
//return data as? NSPasteboardWriting
}
And here is the writeItemsAt indexPaths.
func collectionView(_ collectionView: NSCollectionView, writeItemsAt indexPaths: Set<IndexPath>, to pasteboard: NSPasteboard) -> Bool {
return true
}
A lot of methods were wrong. Here is the corrected code (just the parts related to the collection view delegate and data source to drag and drop items):
ViewDidLoad
var indiceItensMovidosDrag: Set<IndexPath> = []
override func viewDidLoad() {
super.viewDidLoad()
fotosProdutoLojaCollectionView.delegate = self
fotosProdutoLojaCollectionView.dataSource = self
fotosProdutoLojaCollectionView.registerForDraggedTypes([NSPasteboard.PasteboardType(kUTTypeItem as String)])
fotosProdutoLojaCollectionView.setDraggingSourceOperationMask(.move, forLocal: true)
if produtoLoja == nil {
produtoLoja = ProdutoLoja()
}
}
Now the delegate and data source methods methods
func numberOfSections(in collectionView: NSCollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: NSCollectionView, numberOfItemsInSection section: Int) -> Int {
return produtoLoja!.fotos.count
}
func collectionView(_ collectionView: NSCollectionView, itemForRepresentedObjectAt indexPath: IndexPath) -> NSCollectionViewItem {
var item = NSCollectionViewItem()
item = collectionView.makeItem(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "FotoProdutoLojaCollectionViewItem"), for: indexPath)
let fotosProdutoLojaCollectionViewItem = item as! FotoProdutoLojaCollectionViewItem
produtoLoja?.fotos[indexPath.item].foto?.getDataInBackground(block: {
(data: Data?, error: Error?) -> Void in
if error == nil {
fotosProdutoLojaCollectionViewItem.fotoProdutoLojaImageView.image = NSImage(data: data!)
}
})
if produtoLoja!.fotos[indexPath.item].imagemCapa {
fotosProdutoLojaCollectionViewItem.fotoCapaImageView.isHidden = false
}else {
fotosProdutoLojaCollectionViewItem.fotoCapaImageView.isHidden = true
}
return item
}
func collectionView(_ collectionView: NSCollectionView, canDragItemsAt indexPaths: Set<IndexPath>, with event: NSEvent) -> Bool {
return true
}
func collectionView(_ collectionView: NSCollectionView, pasteboardWriterForItemAt indexPath: IndexPath) -> NSPasteboardWriting? {
let retorno = NSPasteboardItem()
var data: Data?
do {
try data = produtoLoja?.fotos[indexPath.item].foto?.getData()
} catch {
}
retorno.setData(data!, forType: NSPasteboard.PasteboardType(kUTTypeItem as String))
return retorno
}
func collectionView(_ collectionView: NSCollectionView, draggingSession session: NSDraggingSession, willBeginAt screenPoint: NSPoint, forItemsAt indexPaths: Set<IndexPath>) {
indiceItensMovidosDrag = indexPaths
}
func collectionView(_ collectionView: NSCollectionView, draggingSession session: NSDraggingSession, endedAt screenPoint: NSPoint, dragOperation operation: NSDragOperation) {
indiceItensMovidosDrag = []
}
func collectionView(_ collectionView: NSCollectionView, validateDrop draggingInfo: NSDraggingInfo, proposedIndexPath proposedDropIndexPath: AutoreleasingUnsafeMutablePointer<NSIndexPath>, dropOperation proposedDropOperation: UnsafeMutablePointer<NSCollectionView.DropOperation>) -> NSDragOperation {
if proposedDropOperation.pointee == NSCollectionView.DropOperation.on {
proposedDropOperation.pointee = NSCollectionView.DropOperation.before
}
return NSDragOperation.move
}
func collectionView(_ collectionView: NSCollectionView, acceptDrop draggingInfo: NSDraggingInfo, indexPath: IndexPath, dropOperation: NSCollectionView.DropOperation) -> Bool {
var retorno = true
if indiceItensMovidosDrag.count == 1 {
for indice in indiceItensMovidosDrag {
collectionView.animator().moveItem(at: indice, to: (indexPath.item <= indice.item) ? indexPath : (IndexPath(item: indexPath.item - 1, section: 0)))
}
} else {
mostrarErro(mensagem: "Erro", informativo: "Só é possível mover uma imagem por vez")
retorno = false
}
//fotosProdutoLojaCollectionView.reloadData()
return retorno
}