index out of range double array collection view - swift

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
}

Related

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

Type 'Workers.Type' has no subscript members Error with Collection Views

I am coding a UIViewCollection to get some data from the Firebase Database.
I am not sure why I am getting this error. I did create a Workers file with all the info to connect to the firebase but still get "Type 'Workers.Type' has no subscript members"
I have the worker file here:
class Workers {
var workerName = ""
var workerFrequency = ""
var workerLocation = ""
var workerNextAppointment = ""
}
My problem is when configuring the collection view mandatory fields. See below;
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return user.count
}
func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 1
}
public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "WorkerCell", for: indexPath) as! WorkerCell
let cellUsers = Workers[indexPath.item]
cell.workerName.text = cellUsers.name
cell.workerLocation.text = ""
return cell
//if let profileImageURL = cellUsers.profileImageURL {
// cell.usersImageView.loadImageUsingCacheWithUrlString(urlString: profileImageURL)
}
}
Anyone knows what is the issue here?

Load specific items in second UI Collection View after tap a cell?

I have a collection view with different category cell. When tap one of this I'd like to load all recipes with that category.
I have two class:
a. CategoryModel - to manage the category
class CategoryModel: NSObject, NSCoding
{
var nameCategory: String
var iconCategory: UIImage
var recipes = [RecipeModel]()
b. RecipeModel
class RecipeModel: NSObject, NSCoding
{
var nameRecipe: String
var quantityRecipe: String
var recipeTime: String
var preparationTime: String
var cookingTime: String
var bakingTempRecipe: String
var difficultyLevelRecipe: String
var imageRecipe: UIImage
var ingredients: [IngredientModel]
var directions: [DirectionModel]
var categoryRecipe: String
I suppose to insert someone in the CategoryCollViewController when I selected the one of all categories... but I don't know to do it!
Someone help me, please!
RecipeCollViewcontroller
class RecipeCollViewController: UICollectionViewController, UITextFieldDelegate
{
var category: CategoryModel!
var recipesList = [RecipeModel]()
struct Storyboard
{
static let leftAndRightPaddings: CGFloat = 2.0
static let numberOfItemsPerRow: CGFloat = 2.0
}
override func viewDidLoad()
{
super.viewDidLoad()
longPressGesture()
RecipeDataManager.shared.recipeController = self
title = category.nameCategory
navigationController?.navigationBar.prefersLargeTitles = true
let collectionViewWidth = collectionView?.frame.width
let itemWidth = (collectionViewWidth! - Storyboard.leftAndRightPaddings) / Storyboard.numberOfItemsPerRow
let layout = collectionViewLayout as! UICollectionViewFlowLayout
layout.itemSize = CGSize(width: itemWidth, height: 250)
}
override func numberOfSections(in collectionView: UICollectionView) -> Int
{
return 1
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
{
return category.recipesList.count
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "RecipeCell", for: indexPath) as! RecipeViewCell
let recipe = category.recipesList[indexPath.item]
cell.labelNameRecipe.text = recipe.nameRecipe
cell.imageViewRecipe.image = recipe.imageRecipe
cell.labelPrepareTime.text = String(recipe.recipeTimeInt)
cell.labelQuantityFor.text = recipe.quantityRecipe
return cell
}
override func collectionView(_ collectionView: UICollectionView, performAction action: Selector, forItemAt indexPath: IndexPath, withSender sender: Any?) {
RecipeDataManager.shared.recipes.remove(at: indexPath.row)
collectionView.deleteItems(at: [indexPath])
}
Declare ** recipesList** in the next screen's ViewController.
var recipesList = [RecipeModel]()
Now in your categoryViewController, implement this CollectionViewDelegate method
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let vc=self.storyboard?.instantiateViewController(withIdentifier: "YourViewControllerIdentifier") as? YourViewControllerClass
recipesList = self.categoryList[indexPath.row].recipes
self.navigationController?.pushViewController(vc!, animated: true)
}
Access your recipes array from ** var recipesList** from declared earlier

How to implement pagination for string array in collection view in swift 4

import UIKit
class ProductListViewController: UIViewController,UICollectionViewDelegate,UICollectionViewDataSource {
#IBOutlet weak var productListCell: UICollectionView!
var manArray = ["Men","m1","m2","m3","m4","m5","m6","m7"]
var mPrice = ["789","1259","959","1625","1259","936","980","1500"]
override func viewDidLoad() {
super.viewDidLoad()
productListCell.delegate = self
productListCell.dataSource = self
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return manArray.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ListCollectionViewCell", for: indexPath) as! ListCollectionViewCell
cell.productImg.image = UIImage(named: manArray[indexPath.row])
cell.productlbl.text = "\(manArray[indexPath.row])"
cell.productprice.text = "\(mPrice[indexPath.row])"
return cell
}
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
if indexPath.row == manArray.count - 1 {
let tmpArray = ["Men","m1","m2","m3","m4","m5","m6","m7"]
let tmpArray2 = ["789","1259","959","1625","1259","936","980","1500"]
manArray.append(contentsOf: tmpArray)
mPrice.append(contentsOf: tmpArray2)
productListCell.reloadData()
}
}
}
Here is my Updated code
Try this code
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
if indexPath.row == manArray.count - 1 {
let tmpArray = ["Men","m1","m2","m3","m4","m5","m6","m7"]
let tmpArray2 = ["789","1259","959","1625","1259","936","980","1500"]
manArray.append(contentsOf: tmpArray)
mPrice.append(contentsOf: tmpArray2)
yourCollectionView.reloadData()
}
}

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
}