Setting images in UITableViewCell in Swift

I have a list of reddit posts that I want to display a thumbnail if it exists. It works for me, but it is very buggy. There are two main problems:

  • Resize image on click
  • Images move in scrolling

This is the code:

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCellWithIdentifier("Post", forIndexPath: indexPath) as UITableViewCell
    let post = swarm.posts[indexPath.row]
    cell.textLabel!.text = post.title

    if(post.thumb? != nil && post.thumb! != "self") {
        cell.imageView!.image = UIImage(named: "first.imageset")
        var image = self.imageCache[post.thumb!]

        if(image == nil) {
            FetchAsync(url: post.thumb!) { data in // code is at bottom, this just drys things up
                if(data? != nil) {
                    image = UIImage(data: data!)
                    self.imageCache[post.thumb!] = image
                    dispatch_async(dispatch_get_main_queue(), {
                        if let originalCell = tableView.cellForRowAtIndexPath(indexPath) {
                            originalCell.imageView?.image = image
                            originalCell.imageView?.frame = CGRectMake(5,5,35,35)
                        }
                    })
                }
            }
        } else {
            dispatch_async(dispatch_get_main_queue(), {
                if let originalCell = tableView.cellForRowAtIndexPath(indexPath) {
                    originalCell.imageView?.image = image
                    originalCell.imageView?.frame = CGRectMake(5,5,35,35)
                }
            })
        }
    }

    return cell
}

      

This is the app when it loads - it looks like everything is working:

loaded up, looks like everything is working

Then if I click on the image (even when scrolling) it changes:

pictures resize on tap

And if you scroll up and down the pictures are getting nasty (look at the middle post - Generics fun):

pictures shuffle on scroll

What am I doing wrong?

** Images and captions are pulled from reddit, not generated by me **


EDIT: FetchAsync class as promised:

class FetchAsync {
    var url: String
    var callback: (NSData?) -> ()

    init(url: String, callback: (NSData?) -> ()) {
        self.url = url
        self.callback = callback
        self.fetch()
    }

    func fetch() {
        var imageRequest: NSURLRequest = NSURLRequest(URL: NSURL(string: self.url)!)
        NSURLConnection.sendAsynchronousRequest(imageRequest,
            queue: NSOperationQueue.mainQueue(),
            completionHandler: { response, data, error in
                if(error == nil) {
                    self.callback(data)
                } else {
                    self.callback(nil)
                }
        })
        callback(nil)
    }
}

      

+3


source to share


3 answers


Unfortunately, this appears to be a limitation of the General table cell. As a result, I created a custom TableViewCell. Oh relied on Ray Wenderlich's tutorial which can be found here: http://www.raywenderlich.com/68112/video-tutorial-table-views-custom-cells

This is a bit of a bummer since the code is so trivial, but I think on the bright side it means a "simple" solution.

My final code:

PostCell.swift

(all scaffolding)



import UIKit

class PostCell: UITableViewCell {

    @IBOutlet weak var thumb: UIImageView!
    @IBOutlet weak var title: UILabel!

    override func awakeFromNib() {
        super.awakeFromNib()
        // Initialization code
    }

    override func setSelected(selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)

        // Configure the view for the selected state
    }

}

      

PostsController.swift

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCellWithIdentifier("PostCell", forIndexPath: indexPath) as PostCell
    let post = swarm.posts[indexPath.row]
    cell.title!.text = post.title

    if(post.thumb? != nil && post.thumb! != "self") {
        cell.thumb!.image = UIImage(named: "first.imageset")
        cell.thumb!.contentMode = .ScaleAspectFit
        var image = self.imageCache[post.thumb!]

        if(image == nil) {
            FetchAsync(url: post.thumb!) { data in
                if(data? != nil) {
                    image = UIImage(data: data!)
                    self.imageCache[post.thumb!] = image
                    dispatch_async(dispatch_get_main_queue(), {
                        if let postCell = tableView.cellForRowAtIndexPath(indexPath) as? PostCell {
                            postCell.thumb!.image = image
                        }
                    })
                }
            }
        } else {
            dispatch_async(dispatch_get_main_queue(), {
                if let postCell = tableView.cellForRowAtIndexPath(indexPath) as? PostCell {
                    postCell.thumb!.image = image
                }
            })
        }
    }

    return cell
}

      

And my worthless storyboard: enter image description here

+5


source


I'm not sure if this is the best way, but here are some solutions:

  • Use AFNetworking like everyone else. It has the idea of ​​an image of the owner of the site, asynchronous loading of the overridden image and smart caching. Install with cocoa pods, create bridge file#import "UIImageView+AFNetworking.h"

  • Create two different types of cells. Before grabbing a cell with dequeReusableCell ... in your cellForRowAtIndexPath, check if it has expanded. If you expand, go back and fill in an expanded cell, otherwise go back and fill in an unexpanded cell. A cell is usually expanded if it is the "selected" cell.



Your mileage may vary

+2


source


It is a huge mistake to call tableView.cellForRowAtIndexPath from the UITableViewDataSource implementation of the tableView (tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell. Instead, when the asynchronous thumb fetch is complete, update the model with the image and then ask the tableView to reload the Rows for that particular indexPath cell. Let your data source define the correct indexPath. If the cell is off-screen when the image finishes loading, there will be no performance impact. And of course reloadRows on the main thread.

0


source







All Articles