Christopher Smit
Christopher Smit

Reputation: 968

UICollectionView Items not cleared properly, reloaded item pasted over old item

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...

enter image description here

Image 2: First Search Initiated...

enter image description here

Image 3: Second search initiated... Text of new venue displayed over other venue

enter image description here

Image 4: Back to default... Text still displayed over other text

enter image description here

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

Answers (3)

Christopher Smit
Christopher Smit

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

Flotmand
Flotmand

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.

https://developer.apple.com/library/content/referencelibrary/GettingStarted/DevelopiOSAppsSwift/CreateATableView.html#//apple_ref/doc/uid/TP40015214-CH8-SW1

// Lars

Upvotes: 0

Mateusz
Mateusz

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

Related Questions