NSCollectionViewDelegate methods aren't getting called - swift

I have an NSCollectionView where I would like to show 6 color wells representing a small color palette. I've set it to have a delegate. I've registered the types that it can drag and drop, but the delegate methods for it aren't getting called.
Here's the class that's both my data source and delegate:
class TileView: NSView, NSCollectionViewDataSource, NSCollectionViewDelegate {
var currentPalette: ColorPalette = .grayscale
#IBOutlet var colorPalettePanel: NSPanel? = nil
#IBOutlet var colorCollection: NSCollectionView? = nil
override func awakeFromNib() {
colorCollection?.registerForDraggedTypes([NSPasteboard.PasteboardType.color])
colorCollection?.setDraggingSourceOperationMask(NSDragOperation.move, forLocal: true)
}
override func draw(_ dirtyRect: NSRect) {
let context = NSGraphicsContext.current?.cgContext
draw(into: context)
}
// MARK: - Collection View Data Source Methods
func collectionView(_ collectionView: NSCollectionView, numberOfItemsInSection section: Int) -> Int {
return 6
}
func collectionView(_ collectionView: NSCollectionView, itemForRepresentedObjectAt indexPath: IndexPath) -> NSCollectionViewItem {
let colorIndex = indexPath.last
let result = ColorCollectionViewItem(nibName: "ColorCollectionViewItem", bundle: Bundle.main, colorIndex: colorIndex!)
let color = gCustomPalette6 [ colorIndex! ]
result.colorWell?.color = NSColor(cgColor: color)!
return result
}
// MARK: - Collection View Delegate Methods
func collectionView(_ collectionView: NSCollectionView, validateDrop draggingInfo: NSDraggingInfo, proposedIndex proposedDropIndex: UnsafeMutablePointer<Int>, dropOperation proposedDropOperation: UnsafeMutablePointer<NSCollectionView.DropOperation>) -> NSDragOperation {
print("dragging Info = \(draggingInfo), proposed drop operation = \(proposedDropOperation)")
return NSDragOperation.move
}
func collectionView(_ collectionView: NSCollectionView, acceptDrop draggingInfo: NSDraggingInfo, index: Int, dropOperation: NSCollectionView.DropOperation) -> Bool {
if dropOperation == .before {
return true
}
return false
}
func collectionView(_ collectionView: NSCollectionView, canDragItemsAt indexes: IndexSet, with event: NSEvent) -> Bool {
return true
}
func collectionView(_ collectionView: NSCollectionView, draggingSession session: NSDraggingSession, willBeginAt screenPoint: NSPoint, forItemsAt indexPaths: Set<IndexPath>) {
print("dragging session = \(session)")
}
}
I've set the delegate in Interface Builder, but none of the delegate methods are ever called.
My delegate object is also the data source and the data source methods all get called as expected. The data source is also set up in Interface Builder. What else do I need to do?

Related

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()
}

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

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

How to properly implement NSCollectionView in swift4?

can you check what's wrong with my codes? I can successfully run the application but with empty content. Thanks!
import Cocoa
class MainViewController: NSViewController {
#IBOutlet weak var scrollView: NSScrollView!
#IBOutlet weak var collectionView: NSCollectionView!
override func viewDidLoad() {
super.viewDidLoad()
// Do view setup here.
self.initView()
self.initCollectionView()
}
func initView() {
let nib = NSNib(nibNamed: NSNib.Name(rawValue: "NavCollectionViewCell"), bundle: nil)
//ßßself.collectionView.register(nib, forItemWithIdentifier: NSUserInterfaceItemIdentifier(rawValue: "NavCollectionViewCell"))
self.collectionView.register(nib, forItemWithIdentifier: .collectionViewItem)
}
func initCollectionView() {
self.collectionView.delegate = self
self.collectionView.dataSource = self
}
}
extension MainViewController: NSCollectionViewDelegate, NSCollectionViewDataSource {
func collectionView(_ collectionView: NSCollectionView, numberOfItemsInSection section: Int) -> Int {
return 3
}
func numberOfSections(in collectionView: NSCollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: NSCollectionView, itemForRepresentedObjectAt indexPath: IndexPath) -> NSCollectionViewItem {
// 4
let item = collectionView.makeItem(withIdentifier: .collectionViewItem, for: indexPath)
return item
}
}
extension NSUserInterfaceItemIdentifier {
static let collectionViewItem = NSUserInterfaceItemIdentifier("NavCollectionViewCell")
}

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
}

Compiler says I'm not conforming to UICollectionViewDelegate; conflicts with Actual Use

I believe I'm following the UICollectionViewDataSource, but the compiler says otherwise.
What am I missing?
import Foundation
import UIKit
class InviteFriendsViewController:UIViewController, UICollectionViewDataSource, UICollectionViewDelegate {
#IBOutlet weak var collectionView: UICollectionView!
#IBAction func navButtonAction(sender: UIBarButtonItem) {
}
#IBAction func segmentedControlAction(sender: UISegmentedControl) {
}
// -----------------------------------------------------------------------------------------------------
// MARK: UICollectionViewDataSource
func collectionView(collectionView: UICollectionView, section: Int) -> Int {
return 1
}
func collectionView(collectionView: UICollectionView, indexPath: NSIndexPath) -> UICollectionViewCell {
let cell: AnyObject = collectionView.dequeueReusableCellWithReuseIdentifier("InviteFriendsVC", forIndexPath:indexPath)
return cell as UICollectionViewCell
}
}
I have two (2) func that covers the required APIs... but I'm getting this compiler error.
Why?
You have to declare your functions like this way:
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int{
return 1
}
// The cell that is returned must be retrieved from a call to -dequeueReusableCellWithReuseIdentifier:forIndexPath:
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell{
let cell: AnyObject = collectionView.dequeueReusableCellWithReuseIdentifier("InviteFriendsVC", forIndexPath:indexPath)
return cell as UICollectionViewCell
}
Try this may be it can help you.