简体   繁体   中英

Swift - Concurrency issue when loading search result. I load the images, send the requests, but the requests are too late

So I have an app that loads movie search results from the web live as the user types. I throttle my search requests so that it only reloads once every 0.3 seconds. (This probably isn't relevant at all, but what the hell). Now my problem is this.

1 - I type in a search term, let's say "Search1". In order to save time, I load up each result's title, year and genre instantly (almost). I keep the poster black, and send an asynchronous request to load the image, because it takes a lot more time. I wait for the image to load.

2 - Before the images load, I then type in another term, let's say "Search2". So I get the text results for "Search2", and maybe some images.

3 - But then the old requests for "Search1" start rolling in, and they replace those of Search2, because they loaded slower. What I get is a combination of old and new images because I couldn't cancel the old requests.

How should I solve this? I need a way to tell the device to stop loading the old images if the user started typing again, and I can't cancel asynchronous requests. How do I fix this?

Code:

  • The cell throttling stuff

    func updateSearchResultsForSearchController(searchController: UISearchController) {

     // to limit network activity, reload 0.3 of a second after last key press. NSObject.cancelPreviousPerformRequestsWithTarget(self, selector: "reload:", object: searchController) if (!UIApplication.sharedApplication().networkActivityIndicatorVisible) { UIApplication.sharedApplication().networkActivityIndicatorVisible = true self.tableView.reloadData() } self.performSelector("reload:", withObject: searchController, afterDelay: 0.3) } 
  • Here is the code that loads up each cell's info:

      override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCellWithIdentifier("cell")! as! CustomCell //Safe-Guard. This shouldn't be needed if I understood what I was doing if (indexPath.row < matchingItems.count) { cell.entry = matchingItems[indexPath.row] //404 /* . . omitted code that loads up text info (title, year, etc.) . . */ //Poster cell.poster.image = nil if let imagePath = matchingItems[indexPath.row]["poster_path"] as? String { //Sessions and Stuff for request let url = NSURL(string: "http://image.tmdb.org/t/p/w185" + imagePath) let urlRequest = NSURLRequest(URL: url!) let session = NSURLSession.sharedSession() //Asynchronous Code: let dataTask = session.dataTaskWithRequest(urlRequest, completionHandler: { (data, response, error) -> Void in if let poster = UIImage(data: data!) { //I want to stop this piece from running if the user starts typing again: dispatch_async(dispatch_get_main_queue()) { //Animate the poster in cell.poster.alpha = 0.0 // hide it cell.poster.image = poster // set it UIView.animateWithDuration(1.0) { cell.poster.alpha = 1.0 // fade it in } } } }) dataTask.resume() } else { //Just use placeholder if no image exists cell.poster.image = UIImage(named: "Placeholder.jpg") } } return cell } 

You can just check the indexPath of the cell and see if it's the same or if it's been dequeued and reused for another another row.

let dataTask = session.dataTaskWithRequest(urlRequest, completionHandler: { (data, response, error) -> Void in
                if let poster = UIImage(data: data!) {
                //I want to stop this piece from running if the user starts typing again:
                    dispatch_async(dispatch_get_main_queue()) {
                        //Animate the poster in
                        if tableView.indexPathForCell(cell) == indexPath {
                            cell.poster.alpha = 0.0     // hide it
                            cell.poster.image = poster  // set it
                            UIView.animateWithDuration(1.0) {
                                cell.poster.alpha = 1.0 // fade it in
                            }
                        }
                    }
                }
            })

For future people stuck with the same issue, I used Alamofire , a networking library that allows you to cancel asynchronous requests. If you use Objective-C then look up AFNetworking. It's the same thing but for Objective-C.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM