Saved object array not showing up in new ViewController - ios

Every time my add button is pressed I am attempting to append that specific item into an array of Item. It prints in the console for each new cell, however when I push to a new ViewController, which will be a summary of all the items added, it does not print the items. Only an empty array is printed.
class CollectionViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout, PostCellDelegate {
var finalList = [Item]()
#objc func addTapped(cell: PostCell) {
guard let indexPath = self.collectionView.indexPath(for: cell) else {return}
hiddenRows.insert(indexPath.row)
cell.removeButton.isHidden = false
let item = itemsArr[indexPath.row]
finalList.append(item)
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellId", for: indexPath) as! PostCell
cell.delegate = self
let item = itemsArr[indexPath.row]
cell.set(name: item.name, brand: item.brand, price: item.price)
print(finalList)
return cell
}
#objc private func handleNext() {
let nextIndex = min(pageControl.currentPage + 1, itemsArr.count - 1)
let indexPath = IndexPath(item: nextIndex, section: 0)
if pageControl.currentPage == 4{
let checkoutView = FinishViewController()
self.navigationController?.pushViewController(checkoutView, animated: true)
checkoutView.modalPresentationStyle = .overCurrentContext
present(checkoutView, animated: true)
print("last item")
}else {
print("not last")
}
pageControl.currentPage = nextIndex
collectionView?.scrollToItem(at: indexPath, at: .centeredHorizontally, animated: true)
}
lazy var pageControl: UIPageControl = {
let pc = UIPageControl()
pc.currentPage = 0
pc.numberOfPages = 4
pc.currentPageIndicatorTintColor = .red
pc.pageIndicatorTintColor = UIColor(red: 249/255, green: 207/255, blue: 224/255, alpha: 1)
return pc
}()
class FinishViewController: UIViewController {
let cV = CollectionViewController()
override func viewDidLoad() {
print(cV.finalList)
super.viewDidLoad()
view.backgroundColor = .red
}

The issue is that in the FinishViewController you are initializing a new instance of CollectionViewController, so the property has the default empty array value.
You need to pass in the array to the FinishViewController when you present or segue to it.
In FinishViewController add the following:
var finalList = [Item]()
And in handleNext make sure you set finalList correctly:
let checkoutView = FinishViewController()
checkoutView.finalList = self.finalList

#objc func addTapped(cell: PostCell) {
guard let indexPath = self.collectionView.indexPath(for: cell) else
{return}
hiddenRows.insert(indexPath.row)
cell.removeButton.isHidden = false
let item = itemsArr[indexPath.row]
finalList.append(item)
collectionView?.reloadData() <---- add this
}

Related

UITableView not updating data

I have trying for sometime to make the "reloadData()" work but it is not working. I've even tried "DispatchQueue.main.async{self.tableView.reloadData()}"
Below is the code
import UIKit
import QuartzCore
import GameKit
enum Tags: Int {
case levelLabel = 100
case background = 101
case highScoreLabel = 102
case star1 = 201
case star2 = 202
case star3 = 203
}
let LightBlue = UIColor.systemBlue
let Green = UIColor.systemRed
let Purple = UIColor.systemPurple
let Pink = UIColor.systemPink
let Orange = UIColor.systemOrange
let OneStarScore = 5
let TwoStarScore = 10
let ThreeStarScore = 15
let NumLevels = 11
class HomeViewController: UIViewController, UITableViewDataSource,
UITableViewDelegate, GKGameCenterControllerDelegate {
#IBOutlet weak var tableView: UITableView!
var highScores: [Int] = [Int]()
var unlockedLevels = 0
let colors = [LightBlue, Green, Purple, Pink, Orange]
var tutorialPageVC: PageDataSourceViewController?
var gameVC: GameViewController?
override func viewDidLoad() {
super.viewDidLoad()
if #available(iOS 13.0, *) {
tableView.separatorColor = UIColor.systemBackground
} else {
// Fallback on earlier versions
}
let storyboard = self.storyboard
tutorialPageVC = storyboard?.instantiateViewController(withIdentifier: "PageDataSourceVC")
as? PageDataSourceViewController
gameVC = storyboard?.instantiateViewController(withIdentifier: "GameViewController")
as? GameViewController
// Trying to fix UITableView
/// Put code here
self.tableView.reloadData()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(false)
let defaults = UserDefaults.standard
if let scoresArray = defaults.array(forKey: "highScores") {
highScores = scoresArray as! [Int]
unlockedLevels = highScores.count - 1 // Subtract one for tutorial
if highScores[unlockedLevels] >= 5 {
unlockedLevels += 1 // Unlock additional level if last high score is greater than 5
}
}
}
// MARK: - TableView data source methods
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return NumLevels
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "LevelCell", for: indexPath)
cell.selectionStyle = UITableViewCell.SelectionStyle.none
let view = cell.viewWithTag(Tags.background.rawValue) as UIView?
let levelLabel = cell.viewWithTag(Tags.levelLabel.rawValue) as! UILabel
let scoreLabel = cell.viewWithTag(Tags.highScoreLabel.rawValue) as! UILabel
let star1 = cell.viewWithTag(Tags.star1.rawValue) as! UIImageView
let star2 = cell.viewWithTag(Tags.star2.rawValue) as! UIImageView
let star3 = cell.viewWithTag(Tags.star3.rawValue) as! UIImageView
view?.layer.borderColor = colors[indexPath.row % 5].cgColor
view?.layer.borderWidth = 6.0
if indexPath.row == 0 {
// Tutorial Level
view?.alpha = 1.0
levelLabel.text = "Tutorial"
star1.isHidden = true
star2.isHidden = true
star3.isHidden = true
scoreLabel.text = ""
} else if indexPath.row + 1 <= highScores.count {
// Levels that have been played
view?.alpha = 1.0
levelLabel.text = "Level \(indexPath.row)"
star1.isHidden = false
star2.isHidden = false
star3.isHidden = false
formatStars(highScores[indexPath.row], stars: [star1, star2, star3])
scoreLabel.text = "High Score: \(highScores[indexPath.row])"
} else {
// Levels that have NOT been played
view?.alpha = indexPath.row > unlockedLevels ? 0.5 : 1.0
levelLabel.text = "Level \(indexPath.row)"
star1.isHidden = true
star2.isHidden = true
star3.isHidden = true
scoreLabel.text = ""
}
return cell
}
/// Display stars for each level on homepage
func formatStars(_ score: Int, stars: [UIImageView]) {
switch score {
case 0..<OneStarScore:
stars[0].image = UIImage(named: "star-empty")
stars[1].image = UIImage(named: "star-empty")
stars[2].image = UIImage(named: "star-empty")
case OneStarScore..<TwoStarScore:
stars[0].image = UIImage(named: "star")
stars[1].image = UIImage(named: "star-empty")
stars[2].image = UIImage(named: "star-empty")
case TwoStarScore..<ThreeStarScore:
stars[0].image = UIImage(named: "star")
stars[1].image = UIImage(named: "star")
stars[2].image = UIImage(named: "star-empty")
default:
stars[0].image = UIImage(named: "star")
stars[1].image = UIImage(named: "star")
stars[2].image = UIImage(named: "star")
}
}
// MARK: - TableView delegate methods
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if indexPath.row == 0 {
self.present(tutorialPageVC!, animated: true, completion: nil)
} else if indexPath.row <= unlockedLevels {
gameVC!.level = indexPath.row
self.present(gameVC!, animated: true, completion: nil)
// performSegueWithIdentifier("LevelSegue", sender: self)
}
}
Maybe the connection between the UITableView and the delegate and data source are not set.
You could do this in the storyboard by ctrl+dragging from the table view to the view controller and setting the delegate and data source outlets.
Or you can do so in code, e.g. in viewDidLoad:
self.tableView.dataSource = self
self.tableView.delegate = self
You need to set dataSource and delegate to self
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.datasource = self
}
And Assign values directly like this
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "LevelCell", for: indexPath) as? LevelCell
cell?.star1.isHidden = true
}

Checkout shopping cart UICollectionViewCell

I have a shopping cart check out flow using a UICollectionView with full page UICollectionViewCells. When the add button is pressed, the remove button is then visible and vice versa. For some reason when add and remove are repeatedly pressed it disrupts the other cells. It will show remove button on another cell when the add button on that cell was never even pressed. I am not sure what's wrong with my logic.
protocol PostCellDelegate {
func removeButtonTapped(cell: PostCell)
func addTapped(cell: PostCell)
}
class PostCell: UICollectionViewCell {
var currentPrice: Float = 0
var delegate: PostCellDelegate?
func set(name: String, brand: String, price: String, image: String){
nameLabel.text = name
brandLabel.text = brand
priceLabel.text = "$\(price)"
photoImageView.loadImage(urlString: image)
}
override init(frame: CGRect) {
super.init(frame: frame)
self.myButton.addTarget(self, action: #selector(addButtonTapped(sender:)), for: .touchUpInside)
self.removeButton.addTarget(self, action: #selector(subButtonTapped(sender:)), for: .touchUpInside)
self.contentView.addSubview(containerView)
setupCellConstraints()
}
#objc func addButtonTapped(sender: UIButton){
self.delegate?.addTapped(cell: self)
sender.isHidden = true
}
#objc func subButtonTapped(sender: UIButton){
self.delegate?.removeButtonTapped(cell: self)
sender.isHidden = true
}
}
class CollectionViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout, PostCellDelegate {
var totalPrice = Float()
private var hiddenRows = Set<Int>()
var finalList = [Item]()
#objc func addTapped(cell: PostCell) {
guard let indexPath = self.collectionView.indexPath(for: cell) else {return}
hiddenRows.insert(indexPath.row)
cell.removeButton.isHidden = false
let item = itemsArr[indexPath.row]
finalList.append(item)
collectionView?.reloadData()
totalPrice += Float(item.price) ?? 0
}
#objc func removeButtonTapped(cell: PostCell) {
guard let indexPath = self.collectionView.indexPath(for: cell) else {return}
hiddenRows.insert(indexPath.row)
cell.myButton.isHidden = false
let item = itemsArr[indexPath.row]
finalList.removeAll{ $0.name == item.name}
totalPrice -= Float(item.price) ?? 0
}
extension CollectionViewController {
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellId", for: indexPath) as! PostCell
cell.delegate = self
let item = itemsArr[indexPath.row]
let page = itemsArr[indexPath.item]
cell.set(name: item.name, brand: item.brand, price: item.price, image: item.image_url)
if hiddenRows.contains(indexPath.row) {
cell.myButton.isHidden = true
cell.removeButton.isHidden = false
}else{
cell.removeButton.isHidden = true
cell.myButton.isHidden = false
}
return cell
}
Cells are reused you need to restore to default here in cellForItemAt
// restore default look here ( hide / show what you need )
if hiddenRows.contains(indexPath.row) {
cell.myButton.isHidden = true
cell.removeButton.isHidden = false
}else{
cell.removeButton.isHidden = true
cell.myButton.isHidden = false
}
It was because in removeButtonTapped I had hiddenRows.insert(indexPath.row) instead of hiddenRows.remove(indexPath.row). I don't know how I missed that.

Detect last cell in full page UICell UICollectionView

I have a checkout flow using a UICollectionViewController and a full page UICell. After the last cell(the last item) is displayed I want to add a summary page of all the items that have been added to the shopping cart along with a total price. I tried to check if the last item = itemsArray.count - 1 however it keeps printing "last item" in the cell before last. Also in the last cell, it prints "not last".
class CollectionViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout, PostCellDelegate {
var totalPrice = Float()
private var hiddenRows = Set<Int>()
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! PostCell
cell.finalLabel.text = String(totalPrice)
cell.delegate = self
let item = itemsArr[indexPath.row]
cell.set(name: item.name, brand: item.brand, price: item.price)
if indexPath.row == itemsArr.count - 1 {
print("last item")
}else {
print("not last")
}
if hiddenRows.contains(indexPath.row) {
cell.myButton.isHidden = true
cell.removeButton.isHidden = false
}else{
cell.removeButton.isHidden = true
cell.myButton.isHidden = false
}
cell.finalLabel.text = String(totalPrice)
return cell
}
#objc func addTapped(cell: PostCell) {
guard let indexPath = self.collectionView.indexPath(for: cell) else {return}
hiddenRows.insert(indexPath.row)
cell.removeButton.isHidden = false
let item = itemsArr[indexPath.row]
print(item.price)
totalPrice += Float(item.price) ?? 0
cell.finalLabel.text = String(totalPrice)
}
#objc func removeButtonTapped(cell: PostCell) {
guard let indexPath = self.collectionView.indexPath(for: cell) else {return}
hiddenRows.insert(indexPath.row)
cell.myButton.isHidden = false
let item = itemsArr[indexPath.row]
totalPrice -= Float(item.price) ?? 0
cell.finalLabel.text = String(totalPrice)
}
}
protocol PostCellDelegate {
func removeButtonTapped(cell: PostCell)
func addTapped(cell: PostCell)
func didPressButton(_ tag: Int)
}
class PostCell: UICollectionViewCell {
var delegate: PostCellDelegate?
func set(name: String, brand: String, price: String){
nameLabel.text = name
brandLabel.text = brand
priceLabel.text = price
}
override init(frame: CGRect) {
super.init(frame: frame)
self.myButton.addTarget(self, action: #selector(addButtonTapped(sender:)), for: .touchUpInside)
self.removeButton.addTarget(self, action: #selector(subButtonTapped(sender:)), for: .touchUpInside)
setupCellConstraints()
}
#objc func buttonPressed(_ sender: UIButton) {
delegate?.didPressButton(sender.tag)
}
#objc func addButtonTapped(sender: UIButton){
self.delegate?.addTapped(cell: self)
sender.isHidden = true
}
#objc func subButtonTapped(sender: UIButton){
self.delegate?.removeButtonTapped(cell: self)
sender.isHidden = true
}
}

How to access album of photos for each row in UITableView

I have a UITableView with few rows. When I hold on a cell the Camera pop-up and I can take photos and to store them in an album of photos.
Each row can have an album of photos. The problem is that when I click on an album, then every time will open me the album with the last picture made and I don't know how to fix this issue with the indexPath.
Here is my code:
class CustomImg: UIImageView {
var indexPath: IndexPath?
}
class ChecklistVC: UIViewController {
lazy var itemSections: [ChecklistItemSection] = {
return ChecklistItemSection.checklistItemSections()
}()
var lastIndexPath: IndexPath!
var currentIndexPath: IndexPath!
...
...
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: Constants.checklistCell, for: indexPath) as! ChecklistCell
let itemCategory = itemSections[indexPath.section]
let item = itemCategory.checklistItems[indexPath.row]
if item.imagesPath!.isEmpty{
cell.defectImageHeightConstraint.constant = 0
}
else{
let thumbnailImage = loadImageFromDiskWith(fileName: item.imagesPath?.last ?? String())
cell.defectImageView.indexPath = indexPath
cell.defectImageView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(tapOnDefectImageView(_:))))
cell.defectImageHeightConstraint.constant = 100
cell.defectImageView.isUserInteractionEnabled = true
cell.defectImageView.image = thumbnailImage
print("For section \(indexPath.section + 1) - row \(String(describing: indexPath.row + 1)) the album photos are: \(String(describing: item.imagesPath))")
}
return cell
}
#objc func tapOnDefectImageView(_ sender: UITapGestureRecognizer){
guard let img = sender.view as? CustomImg, let indexPath = img.indexPath else { return }
currentIndexPath = indexPath
let listImagesDefectVC = storyboard?.instantiateViewController(withIdentifier: "ListImagesDefectID") as! ListImagesDefectVC
let item = itemSections[indexPath.section].checklistItems[indexPath.row]
listImagesDefectVC.listImagesPath = item.imagesPath
listImagesDefectVC.isPhotoAccessedFromChecklist = true
listImagesDefectVC.delegate = self
navigationController?.pushViewController(listImagesDefectVC, animated: true)
}
// A menu from where the user can choose to take pictures for "Vehicle Damage/Defects" or "Trailer Damage/Defects"
func showOptionsForAddPhoto(_ indexPath: IndexPath){
let addPhotoForVehicle = UIAlertAction(title: "Add photo for Vehicle", style: .default) { action in
self.lastIndexPath = indexPath // Get the position of the cell where to add the vehicle photo
self.showCamera(imagePicker: self.imagePicker)
}
let addPhotoForTrailer = UIAlertAction(title: "Add photo for Trailer", style: .default) { action in
self.lastIndexPath = indexPath
self.showCamera(imagePicker: self.imagePicker)
}
let actionSheet = configureActionSheet()
actionSheet.addAction(addPhotoForVehicle)
actionSheet.addAction(addPhotoForTrailer)
self.present(actionSheet, animated: true, completion: nil)
}
// Get the list of the images from ListImagesDefectVC
extension ChecklistVC: ListImagesDefectDelegate {
func receiveListImagesUpdated(imagesFromList: [String]?) {
print("Received Array: \(imagesFromList ?? [])")
let item = itemSections[currentIndexPath.section].checklistItems[currentIndexPath.row]
item.imagesPath = imagesFromList
}
}
}
Here is a GIF with my actual issue. In this capture I click only on Photo 1 and Photo 3. And every time Photo 2 take the value of what I clicked before:
http://g.recordit.co/VMeGZbf7TF.gif
Thank you if you are reading this.
I guess in tapOnDefectImageView you should use the clicked indexPath for the cell not lastIndexPath which is the reason why clicking a row shows photos of last clicked indexPath
so either add this gesture inside the cell and in the action method do
delegate?.tapOnDefectImageView(self) //// self = cell
and use
#objc func tapOnDefectImageView(_ gest:ChecklistCell){
guard let indexPath = tableView.indexPath(cell) else { return }
let listImagesDefectVC = storyboard?.instantiateViewController(withIdentifier: "ListImagesDefectID") as! ListImagesDefectVC
let item = itemSections[indexPath.section].checklistItems[indexPath.row]
listImagesDefectVC.listImagesPath = item.imagesPath
listImagesDefectVC.isPhotoAccessedFromChecklist = true
listImagesDefectVC.delegate = self
navigationController?.pushViewController(listImagesDefectVC, animated: true)
}
or create
class CustomImg:UIImageView {
var indexPath:IndexPath?
}
with this inside cellForRowAt
cell.defectImageView.indexPath = indexPath
cell.defectImageView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(tapOnDefectImageView)))
then assign the class to the imageView of the cell and now you can do
#objc func tapOnDefectImageView(_ sender:UITapGestureRecognizer){
guard let img = sender.view as? CustomImg , let indexPath = img.indexPath else { return }
let listImagesDefectVC = storyboard?.instantiateViewController(withIdentifier: "ListImagesDefectID") as! ListImagesDefectVC
let item = itemSections[indexPath.section].checklistItems[indexPath.row]
listImagesDefectVC.listImagesPath = item.imagesPath
listImagesDefectVC.isPhotoAccessedFromChecklist = true
listImagesDefectVC.delegate = self
navigationController?.pushViewController(listImagesDefectVC, animated: true)
}

Collection views cells are not appearing in collection view?

This is my first time working with collection views and I am struggling quite a bit. I set up a collection view layout in the "setupView" function, and then implemented the typical view methods, like numberOfSections and cellForRowAtIndexPath. Right now, I'm just trying to get the collection view to work so I'm just using an image that I already have in my workspace, instead of getting pictures from an API (that will come later).
Here is the code I am using. Why is it not displaying anything other than a black screen ?
import UIKit
import Alamofire
import AlamofireImage
class PhotoBrowserCollectionViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout {
let goldenWordsYellow = UIColor(red: 247.00/255.0, green: 192.00/255.0, blue: 51.00/255.0, alpha: 0.5)
#IBOutlet weak var menuButton:UIBarButtonItem!
#IBOutlet var picturesCollectionView: UICollectionView!
var pictureObjects = NSMutableOrderedSet(capacity: 1000)
var customRefreshControl = UIRefreshControl()
let PhotoBrowserCellIdentifier = "PhotoBrowserCell"
var dateFormatter = NSDateFormatter()
var nodeIDArray = NSMutableArray()
var timeStampDateString : String!
override func viewDidLoad() {
super.viewDidLoad()
self.collectionView!.registerClass(PhotoBrowserCollectionViewCell.self, forCellWithReuseIdentifier: PhotoBrowserCellIdentifier)
self.revealViewController().rearViewRevealWidth = 280
collectionView!.delegate = self
collectionView!.dataSource = self
customRefreshControl = UIRefreshControl()
customRefreshControl.backgroundColor = goldenWordsYellow
customRefreshControl.tintColor = UIColor.whiteColor()
self.picturesCollectionView!.addSubview(customRefreshControl)
navigationController?.setNavigationBarHidden(false, animated: true)
navigationItem.title = "Pictures"
setupView()
populatePhotos()
self.dateFormatter.dateFormat = "dd/MM/yy"
}
override func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 1
}
override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 5 // setting an arbitrary value in case pictureObjects is not getting correctly populated
}
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(PhotoBrowserCellIdentifier, forIndexPath: indexPath) as! PhotoBrowserCollectionViewCell
cell.imageView.image = UIImage(named: "mail")
return cell
}
And here is my setupView function.
override func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
performSegueWithIdentifier("ShowPhoto", sender: (self.pictureObjects.objectAtIndex(indexPath.item) as! PictureElement).imageURL)
}
func setupView() {
navigationController?.setNavigationBarHidden(false, animated: true)
let layout = UICollectionViewFlowLayout()
let itemWidth = (view.bounds.size.width - 2) / 3
layout.itemSize = CGSize(width: itemWidth, height: itemWidth)
layout.minimumInteritemSpacing = 1.0
layout.minimumLineSpacing = 1.0
collectionView!.collectionViewLayout = layout
navigationItem.title = "Pictures"
customRefreshControl.tintColor = UIColor.whiteColor()
customRefreshControl.addTarget(self, action: "handleRefresh", forControlEvents: .ValueChanged)
self.collectionView!.addSubview(customRefreshControl)
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
return CGSizeMake(120, 120)
}
Everything looks really messed up in the storyboard. I'm using a UICollectionViewController swift class (not a UIViewController + collectionView combo), and I don't really know which outlets I should be using. Here is my list of connections on the entire view controller:
And finally, here is my very simple view hierarchy.
I truly have no idea why the collection view isn't working properly. Any ideas ?
EDIT 1: I changed a lot of code and ended up getting my collectionView to work. For those who are struggling with a similar issue, here is the entirety of the code I am using for the collectionView. Maybe it will help you fix problems in your own collectionView code
import UIKit
import Alamofire
import AlamofireImage
class PhotoBrowserCollectionViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout {
let goldenWordsYellow = UIColor(red: 247.00/255.0, green: 192.00/255.0, blue: 51.00/255.0, alpha: 0.5)
#IBOutlet weak var menuButton:UIBarButtonItem!
#IBOutlet var picturesCollectionView: UICollectionView!
var temporaryPictureObjects = NSMutableOrderedSet(capacity: 1000)
var pictureObjects = NSMutableOrderedSet(capacity: 1000)
var goldenWordsRefreshControl = UIRefreshControl()
var revealViewControllerIndicator : Int = 0
let imageCache = NSCache()
var customView: UIView!
var labelsArray: [UILabel] = []
var isAnimating = false
var currentColorIndex = 0
var currentLabelIndex = 0
var timer : NSTimer!
var populatingPhotos = false
var currentPage = 0
let PhotoBrowserCellIdentifier = "PhotoBrowserCell"
var dateFormatter = NSDateFormatter()
var nodeIDArray = NSMutableArray()
var timeStampDateString : String!
var cellLoadingIndicator = UIActivityIndicatorView()
var scrollViewDidScrollLoadingIndicator = UIActivityIndicatorView()
override func viewDidLoad() {
super.viewDidLoad()
self.collectionView!.registerClass(PhotoBrowserCollectionViewCell.self, forCellWithReuseIdentifier: PhotoBrowserCellIdentifier)
self.cellLoadingIndicator.backgroundColor = goldenWordsYellow
self.cellLoadingIndicator.hidesWhenStopped = true
if self.revealViewController() != nil {
revealViewControllerIndicator = 1
menuButton.target = self.revealViewController()
menuButton.action = "revealToggle:"
self.view.addGestureRecognizer(self.revealViewController().panGestureRecognizer())
}
self.revealViewController().rearViewRevealWidth = 280
collectionView!.delegate = self
collectionView!.dataSource = self
goldenWordsRefreshControl = UIRefreshControl()
goldenWordsRefreshControl.backgroundColor = goldenWordsYellow
goldenWordsRefreshControl.tintColor = UIColor.whiteColor()
self.collectionView!.addSubview(goldenWordsRefreshControl)
navigationController?.setNavigationBarHidden(false, animated: true)
navigationItem.title = "Pictures"
setupView()
populatePhotos()
self.dateFormatter.dateFormat = "dd/MM/yy"
self.cellLoadingIndicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyle.Gray
self.cellLoadingIndicator.color = goldenWordsYellow
self.cellLoadingIndicator.center = (self.collectionView?.center)!
self.collectionView!.addSubview(cellLoadingIndicator)
self.collectionView!.bringSubviewToFront(cellLoadingIndicator)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func scrollViewDidEndDecelerating(scrollView: UIScrollView) {
if goldenWordsRefreshControl.refreshing {
if !isAnimating {
holdRefreshControl()
}
}
}
func holdRefreshControl() {
timer = NSTimer.scheduledTimerWithTimeInterval(2.0, target: self, selector: "handleRefresh", userInfo: nil, repeats: true)
}
override func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 1
}
override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return (pictureObjects.count) // setting an arbitrary value in case pictureObjects is not getting correctly populated
}
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(PhotoBrowserCellIdentifier, forIndexPath: indexPath) as! PhotoBrowserCollectionViewCell
if let pictureObject = pictureObjects.objectAtIndex(indexPath.row) as? PictureElement {
let title = pictureObject.title ?? "" // if pictureObject.title == nil, then we return an empty string
let timeStampDateObject = NSDate(timeIntervalSince1970: NSTimeInterval(pictureObject.timeStamp))
let timeStampDateString = dateFormatter.stringFromDate(timeStampDateObject)
let author = pictureObject.author ?? ""
let issueNumber = pictureObject.issueNumber ?? ""
let volumeNumber = pictureObject.volumeNumber ?? ""
let nodeID = pictureObject.nodeID ?? 0
let imageURL = pictureObject.imageURL ?? "http://goldenwords.ca/sites/all/themes/custom/gw/logo.png"
cell.request?.cancel()
if let image = self.imageCache.objectForKey(imageURL) as? UIImage {
cell.imageView.image = image
} else {
cell.imageView.image = nil
cell.request = Alamofire.request(.GET, imageURL).responseImage() { response in
if let image = response.result.value {
self.imageCache.setObject(response.result.value!, forKey: imageURL)
if cell.imageView.image == nil {
cell.imageView.image = image
}
}
}
}
}
return cell
}
self.performSegueWithIdentifier("ShowPhoto", sender: self)
}
func setupView() {
navigationController?.setNavigationBarHidden(false, animated: true)
let layout = UICollectionViewFlowLayout()
let itemWidth = (view.bounds.size.width) / 3
layout.itemSize = CGSize(width: itemWidth, height: itemWidth)
layout.minimumInteritemSpacing = 1.0
layout.minimumLineSpacing = 1.0
collectionView!.collectionViewLayout = layout
navigationItem.title = "Pictures"
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
return CGSizeMake(((self.collectionView?.frame.width)!*0.5)-2, self.collectionView!.frame.height/3)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "ShowPhoto" {
let detailViewController = segue.destinationViewController as! PhotoViewerViewController
let indexPaths = self.collectionView!.indexPathsForSelectedItems()
let indexPath = indexPaths![0] as! NSIndexPath
let item = indexPath.item
}
}
override func scrollViewDidScroll(scrollView: UIScrollView) {
if (scrollView.contentOffset.y + view.frame.size.height > scrollView.contentSize.height * 0.75) {
populatePhotos()
}
}
func populatePhotos() {
if populatingPhotos {
return
}
populatingPhotos = true
self.cellLoadingIndicator.startAnimating()
self.temporaryPictureObjects.removeAllObjects()
Alamofire.request(GWNetworking.Router.Pictures(self.currentPage)).responseJSON() { response in
if let JSON = response.result.value {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0)) {
var nodeIDArray : [Int]
if (JSON .isKindOfClass(NSDictionary)) {
for node in JSON as! Dictionary<String, AnyObject> {
let nodeIDValue = node.0
var lastItem : Int = 0
self.nodeIDArray.addObject(nodeIDValue)
if let pictureElement : PictureElement = PictureElement(title: "Picture", nodeID: 0, timeStamp: 0, imageURL: "http://goldenwords.ca/sites/all/themes/custom/gw/logo.png", author: "Staff", issueNumber: "Issue # error", volumeNumber: "Volume # error") {
pictureElement.title = node.1["title"] as! String
pictureElement.nodeID = Int(nodeIDValue)!
let timeStampString = node.1["revision_timestamp"] as! String
pictureElement.timeStamp = Int(timeStampString)!
if let imageURL = node.1["image_url"] as? String {
pictureElement.imageURL = imageURL
}
if let author = node.1["author"] as? String {
pictureElement.author = author
}
if let issueNumber = node.1["issue_int"] as? String {
pictureElement.issueNumber = issueNumber
}
if let volumeNumber = node.1["volume_int"] as? String {
pictureElement.volumeNumber = volumeNumber
}
if self.pictureObjects.containsObject(pictureElement) {
// do not add the pictureElement to the set of pictures
} else {
lastItem = self.temporaryPictureObjects.count // Using a temporary set to not handle the dataSource set directly (safer).
self.temporaryPictureObjects.addObject(pictureElement)
}
let indexPaths = (lastItem..<self.temporaryPictureObjects.count).map { NSIndexPath(forItem: $0, inSection: 0) }
}
}
let timeStampSortDescriptor = NSSortDescriptor(key: "timeStamp", ascending: false)
self.temporaryPictureObjects.sortUsingDescriptors([timeStampSortDescriptor])
}
dispatch_async(dispatch_get_main_queue()) {
for object in self.temporaryPictureObjects {
self.pictureObjects.addObject(object)
}
self.temporaryPictureObjects.removeAllObjects()
self.collectionView!.reloadData()
self.cellLoadingIndicator.stopAnimating()
self.goldenWordsRefreshControl.endRefreshing()
self.currentPage++
self.populatingPhotos = false
}
}
}
}
}
func handleRefresh() {
goldenWordsRefreshControl.beginRefreshing()
self.pictureObjects.removeAllObjects()
self.temporaryPictureObjects.removeAllObjects()
self.collectionView!.reloadData()
self.currentPage = 0
self.picturesCollectionView.bringSubviewToFront(cellLoadingIndicator)
self.populatingPhotos = false
populatePhotos()
}
}
Simply reload your collectionView after the data is formatted.
picturesCollectionView.reloadData()
override func viewDidLoad() {
super.viewDidLoad()
self.collectionView!.registerClass(PhotoBrowserCollectionViewCell.self, forCellWithReuseIdentifier: PhotoBrowserCellIdentifier)
self.revealViewController().rearViewRevealWidth = 280
collectionView!.delegate = self
collectionView!.dataSource = self
customRefreshControl = UIRefreshControl()
customRefreshControl.backgroundColor = goldenWordsYellow
customRefreshControl.tintColor = UIColor.whiteColor()
self.picturesCollectionView!.addSubview(customRefreshControl)
navigationController?.setNavigationBarHidden(false, animated: true)
navigationItem.title = "Pictures"
setupView()
populatePhotos()
self.dateFormatter.dateFormat = "dd/MM/yy"
picturesCollectionView.reloadData()
}

Resources