1

I have added a table view, and I am display image in the cells. I have also added this code:

tableView.estimatedRowHeight = 175
tableView.rowHeight = UITableViewAutomaticDimension

So that the cells resize depending on the image.

When I launch my app though, I get this :

enter image description here

And the images do not load untill I start scrolling...If I scroll down half the page then go back to the top, I get this(which is correct):

enter image description here

Also if I remove the like button and just has the image alone in a cell, when I launch my app if I wait 3 seconds without touching anything the cells resize on they're own..?!

Any ideas? I have researched on google and tried the odd solution for the older versions of Xcode, But nothing seems to work!

Here is the rest of my code from the TableViewController:

extension TimelineViewController: UITableViewDataSource {

    func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
        return 46
    }

    func numberOfSectionsInTableView(tableView: UITableView) -> Int {
        return timelineComponent.content.count
    }

    func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
        let headerCell = tableView.dequeueReusableCellWithIdentifier("PostHeader") as! PostHeaderTableViewCell

        let post = self.timelineComponent.content[section]
        headerCell.post = post


        return headerCell
    }


    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 1
    }

    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCellWithIdentifier("PostCell") as! PostTableViewCell

        //cell.postImageView?.image = UIImage(named: "Background.png")

        let post = timelineComponent.content[indexPath.section]
        post.downloadImage()
        post.fetchLikes()
        cell.post = post

        cell.layoutIfNeeded()
        return cell
    }
}

extension TimelineViewController: UITableViewDelegate {
    func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
        timelineComponent.targetWillDisplayEntry(indexPath.section)
    }


Download image code:

func downloadImage() {
    // 1
    image.value = Post.imageCache[self.imageFile!.name]

    if image is not downloaded yet, get it
        if (image.value == nil) {

            imageFile?.getDataInBackgroundWithBlock { (data: NSData?, error: NSError?) -> Void in
                if let data = data {
                    let image = UIImage(data: data, scale: 2.0)!
                    self.image.value = image
                    // 2
                    Post.imageCache[self.imageFile!.name] = image
                }
            }
        }
}

// MARK: PFSubclassing
extension Post: PFSubclassing {
    static func parseClassName() -> String {
        return "Post"
    }

    override class func initialize() {
        var onceToken : dispatch_once_t = 0;
        dispatch_once(&onceToken) {
            // inform Parse about this subclass
            self.registerSubclass()
            // 1
            Post.imageCache = NSCacheSwift<String, UIImage>()
        }
    }
}


And here is my TableViewCell:


var post: Post? {
    didSet {
        postDisposable?.dispose()
        likeDisposable?.dispose()

        if let oldValue = oldValue where oldValue != post {

            oldValue.image.value = nil

        }

        if let post = post {

            postDisposable = post.image
            .bindTo(postImageView.bnd_image)

            likeDisposable = post.likes
            .observe { (value: [PFUser]?) -> () in

                if let value = value {
                    //self.likesLabel.text = self.stringFromUserList(value)
                    self.likeButton.selected = value.contains(PFUser.currentUser()!)
                    // self.likesIconImageView.hidden = (value.count == 0)
                } else {
                    //self.likesLabel.text = ""
                    self.likeButton.selected = false
                    //self.likesIconImageView.hidden = true

                }
            }
        }
    }
}

Any help is really appreciated!

Bista
  • 7,869
  • 3
  • 27
  • 55

1 Answers1

2

I guess, you need to reload the cell when the image is finally loaded, because tableView needs to recalculate cell height (and the whole contentHeight) when image with new size arrives

post.downloadImage { _ in
    if tableView.indexPathForCell(cell) == indexPath {
        tableView.reloadRowsAtIndexPaths([indexPath], animation: .None)
    }
}

and downloadImage method needs to call completion closure. Something like that.

func downloadImage(completion: ((UIImage?) -> Void)?) {

    if let imageValue = Post.imageCache[self.imageFile!.name] {
        image.value = imageValue
        completion?(imageValue)
        return
    }

    //if image is not downloaded yet, get it
    imageFile?.getDataInBackgroundWithBlock { (data: NSData?, error: NSError?) -> Void in
        if let data = data {
            let image = UIImage(data: data, scale: 2.0)!
            self.image.value = image
            // 2
            Post.imageCache[self.imageFile!.name] = image

            completion?(image)
        } else {
            completion?(nil)
        }
    }
}
Anton Tyutin
  • 626
  • 1
  • 5
  • 15
  • 1
    Thanks for answering, would you mind just explaing that..I'm quite new and dont really understand how that'll work. –  Jan 14 '16 at 17:30
  • 1
    When you call reloadRowsAtIndexPaths, tableView recalculates heights for these rows, because at the first time there was no image, and it couldn't calculate the actual size. That's what you need. About indexPaths: when you scroll tableView it reuses cells again and again. And when the image is finally downloaded, the cell can already be 5 times reused and it doesn't need to be reloaded – Anton Tyutin Jan 14 '16 at 17:38
  • 1
    And should I place the code you added above to the cellForRowAtIndexPath? –  Jan 14 '16 at 17:39
  • 1
    I've updated my answer, and yes, the first example was for cellForRowAtIndexPath – Anton Tyutin Jan 14 '16 at 17:45
  • 1
    Well, its defenitely better than previously! but once its loaded you can see it resize and looks a bit jumpy, not too sure how to explain it! but when it loads the homepage you can see it all resize, is there anyway of hiding that or something? if so I'll open a new question..? –  Jan 14 '16 at 17:53
  • 1
    Thanks alot for that anyway though!! –  Jan 14 '16 at 17:54
  • 1
    I guess, you can animate them or leave them as they are. As I understand how it works in cool apps, they either have the same size for all cells of same type, or they get image sizes before they load actual images – Anton Tyutin Jan 14 '16 at 17:56
  • 1
    How would I get the actual image size before loading them? (Thats how the new instagram does it? now that they have different sized images and not square) –  Jan 14 '16 at 17:57
  • Don't know. I can only guess. But I think they preload metadata that contains image sizes for a batch of posts – Anton Tyutin Jan 14 '16 at 18:00