The problem:
I have a CollectionView which loads a UIimage into each cell. However my problem is that when I load additional cells with more images they appear to duplicate. I can't quite see what could be causing this in my code. Might it be because of some problem with reusable cells?
Can anyone see why this occurs?
NOTE: The array with the images doesn't have duplicates
Video of problem: https://www.youtube.com/watch?v=vjRsFc8DDmI
Image of the problem:
Here are my functions for collectionView:
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
//#warning Incomplete method implementation -- Return the number of items in the section
if self.movies == nil
{
return 0
}
return self.movies!.count
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell
{
let cell =
collectionView.dequeueReusableCellWithReuseIdentifier(reuseIdentifier,
forIndexPath: indexPath) as! UpcomingCollectionViewCell
if self.movies != nil && self.movies!.count >= indexPath.row
{
// Calc size of cell
cell.frame.size.width = screenWidth / 3
cell.frame.size.height = screenWidth / 3 * 1.54
let movies = self.movies![indexPath.row]
if(movies.posterPath != "" || movies.posterPath != "null"){
cell.data = movies.posterPath
}
else{
cell.data = nil
}
// See if we need to load more movies
let rowsToLoadFromBottom = 5;
let rowsLoaded = self.movies!.count
if (!self.isLoadingMovies && (indexPath.row >= (rowsLoaded - rowsToLoadFromBottom)))
{
let totalRows = self.movieWrapper!.totalResults!
let remainingMoviesToLoad = totalRows - rowsLoaded;
if (remainingMoviesToLoad > 0)
{
self.loadMoreMovies()
}
}
}
else
{
cell.data = nil
}
return cell
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize
{
return CGSize(width: screenWidth/3, height: screenWidth/3*1.54)
}
Here I load the data from a Wrapper class:
func loadFirstMovies()
{
isLoadingMovies = true
Movies.getMovies({ (movieWrapper, error) in
if error != nil
{
// TODO: improved error handling
self.isLoadingMovies = false
let alert = UIAlertController(title: "Error", message: "Could not load first movies \(error?.localizedDescription)", preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "Click", style: UIAlertActionStyle.Default, handler: nil))
self.presentViewController(alert, animated: true, completion: nil)
}
self.addMoviesFromWrapper(movieWrapper)
self.activityIndicator.hidden = true
self.isLoadingMovies = false
self.collectionView.reloadData()
})
}
func loadMoreMovies(){
self.isLoadingMovies = true
if self.movies != nil && self.movieWrapper != nil && self.movieWrapper!.page < self.movieWrapper!.totalPages
{
// there are more species out there!
Movies.getMoreMovies(self.movieWrapper, completionHandler: { (moreWrapper, error) in
if error != nil
{
// TODO: improved error handling
self.isLoadingMovies = false
let alert = UIAlertController(title: "Error", message: "Could not load more movies \(error?.localizedDescription)", preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "Click", style: UIAlertActionStyle.Default, handler: nil))
self.presentViewController(alert, animated: true, completion: nil)
}
print("got more!")
self.addMoviesFromWrapper(moreWrapper)
self.isLoadingMovies = false
self.collectionView.reloadData()
})
}
}
func addMoviesFromWrapper(wrapper: MovieWrapper?)
{
self.movieWrapper = wrapper
if self.movies == nil
{
self.movies = self.movieWrapper?.results
}
else if self.movieWrapper != nil && self.movieWrapper!.results != nil
{
self.movies = self.movies! + self.movieWrapper!.results!
}
}
And lastly I call: loadFirstMovies()
in viewDidLoad()
EDIT: The UpcomingCollectionViewCell
class UpcomingCollectionViewCell: UICollectionViewCell {
@IBOutlet weak var imageView: UIImageView!
var data:String?{
didSet{
self.setupData()
}
}
func setupData(){
self.imageView.image = nil // reset the image
if let urlString = data{
let url = NSURL(string: "http://image.tmdb.org/t/p/w342/" + urlString)
self.imageView.hnk_setImageFromURL(url!)
}
}
}
This is a typical table view/collection view setup issue.
Any time you recycle a cell using a dequeue method like dequeueReusableCellWithReuseIdentifier:
, you must always fully configure all the views in the cell, including setting all the text fields/image views to their starting values. Your code has several if statements where, if the condition of the if is false, you don't set up the views in the cell. You need to have else clauses that clear out old content from the cell's views in case there is content left over from the last time the cell was used.
**EDIT:
Change your cellForItemAtIndexPath
method to begin like this:
func collectionView(collectionView: UICollectionView,
cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell
{
let cell =
collectionView.dequeueReusableCellWithReuseIdentifier(reuseIdentifier,
forIndexPath: indexPath) as! UpcomingCollectionViewCell
cell.imageView.image = nil; //Remove the image from the recycled cell
//The rest of your method ...