Reputation: 968
I am banging my head against the wall with the following issue: I have a UICollectionView that displays a list of venues. The user has an option to search by venue name or city. The searching is working perfectly, but the moment the UICollectionView is reloaded with the new search results, the new items gets pasted over the old items. The strange part is, the UICollectionView does seem to be cleared, since all other Items gets removed except for the very first item. See my images below:
Image 1: No Search initiated yet...
Image 2: First Search Initiated...
Image 3: Second search initiated... Text of new venue displayed over other venue
Image 4: Back to default... Text still displayed over other text
Below is my code that retrieves the venues:
func loadVenuesBasedOnSearch(searchString: String) {
// Retrieve server URL
let serverUrl = DBConnection().returnConnectionUrl()
// Send HTTP GET Request
// Define server side script URL
let scriptUrl = serverUrl + "bmc_api.php"
// Add one parameter just to avoid caching
let urlWithParams = scriptUrl + "?request=retrieveVenuesBySearch&searchString=\(searchString)&province="
// Create NSURL Object
let myUrl = URL(string: urlWithParams + province!.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!);
// Creaste URL Request
var request = URLRequest(url:myUrl!)
// Set request HTTP method to GET. It could be POST as well
request.httpMethod = "GET"
// Excute HTTP Request
let task = URLSession.shared.dataTask(with: request) {
data, response, error in
// Check for error
if error != nil
{
self.noVenues.layer.zPosition = 1
self.noVenues.isHidden = false
return
}
// Convert server json response to NSDictionary
do {
if let convertedJsonIntoArray = try JSONSerialization.jsonObject(with: data!, options: []) as? NSArray {
self.venues = convertedJsonIntoArray as [AnyObject]
let activityViewController = ActivityViewController(message: "Retrieving Venues...")
DispatchQueue.main.async { [unowned self] in
self.present(activityViewController, animated: true, completion: nil)
self.myCollectionView!.reloadData()
}
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
activityViewController.dismiss(animated: true, completion: nil)
}
}
} catch _ as NSError {
self.noVenues.layer.zPosition = 1
self.noVenues.isHidden = false
}
}
task.resume()
}
And the code to display the items:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let myCell = collectionView.dequeueReusableCell(withReuseIdentifier: "MyCell", for: indexPath as IndexPath)
//Customize Collection View Cell
myCell.backgroundColor = UIColor(red: 64/255, green: 64/255, blue: 64/255, alpha: 1.0)
myCell.layer.shadowColor = UIColor.black.cgColor
myCell.layer.shadowOffset = CGSize(width: 4, height: 4)
myCell.layer.shadowOpacity = 0.3
myCell.layer.shadowRadius = 2.0
myCell.clipsToBounds = false
myCell.layer.masksToBounds = false
//Retrieve string values from Json Object
let venueDictionary = self.venues[indexPath.row] as! NSDictionary
let imageUrlString = venueDictionary.object(forKey: "venue_logo") as! String
let venueName = venueDictionary.object(forKey: "venue_name") as! String
let venueLocation = venueDictionary.object(forKey: "venue_location") as! String
let imageUrl:NSURL = NSURL(string: imageUrlString)!
DispatchQueue.global(qos: .userInitiated).async {
let imageData:NSData = NSData(contentsOf: imageUrl as URL)!
let imageView = UIImageView(frame: CGRect(x: 5, y: 5, width: myCell.frame.size.width - 10, height: myCell.frame.size.height - 60))
let textVenueName = UILabel(frame: CGRect(x: 10, y: 85, width:myCell.frame.size.width, height: myCell.frame.size.height))
let textVenueLocation = UILabel(frame: CGRect(x: 10, y: 110, width:myCell.frame.size.width, height: myCell.frame.size.height))
DispatchQueue.main.async {
let image = UIImage(data: imageData as Data)
imageView.image = image
imageView.contentMode = UIViewContentMode.scaleToFill
textVenueName.text = venueName
textVenueName.textColor = UIColor.white
textVenueName.font = UIFont.boldSystemFont(ofSize: 16.0)
textVenueLocation.text = venueLocation
textVenueLocation.textColor = UIColor.white
textVenueLocation.font = UIFont.boldSystemFont(ofSize: 12.0)
myCell.addSubview(imageView)
myCell.addSubview(textVenueName)
myCell.addSubview(textVenueLocation)
}
}
return myCell
}
I'd really appreciate any help or if you could simply tell me what I am missing and what I need to look into further. Please note that I am a new Swift developer so my knowledge is a little limited, so sorry if I am making obvious errors.
Upvotes: 0
Views: 79
Reputation: 968
Okay so after some playing around I solved it. I have to thank Mateusz for pointing out that my views are being recreated, so I added this piece of code before creating the views:
let subViews = myCell.subviews
for subview in subViews{
if (subview is UILabel) {
print("removing..")
subview.removeFromSuperview()
}
if (subview is UIImageView) {
print("removing..")
subview.removeFromSuperview()
}
}
This removes the old created views and add the new ones without pasting over anything.
Upvotes: 0
Reputation: 41
I wanted to just add this as a comment to Mateusz, because I know it is just a link for further reading, but I am not allowed due to lack of reputations point.
I am also fairly new to iOS development, and have used Apples "Getting started"-guide, which does a good job explaining how TableViews works. CollectionsViews are basically the same, so I would recommend you to read this chapter and see how they create the cells in the storyboard.
// Lars
Upvotes: 0
Reputation: 1224
It's because cells are reusable in Collection view. In your code you are creating new views so after reload in cell on first position you have more labels.
Instead creating new views (UIImageView, UILabel) you should create them on xib (storyboard) and update each time.
Upvotes: 1