Welcome to the Treehouse Community

The Treehouse Community is a meeting place for developers, designers, and programmers of all backgrounds and skill levels to get support. Collaborate here on code errors or bugs that you need feedback on, or asking for an extra set of eyes on your latest project. Join thousands of Treehouse students and alumni in the community today. (Note: Only Treehouse students can comment or ask questions, but non-students are welcome to browse our conversations.)

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and a supportive community. Start your free trial today.

iOS

jean-marie castellucci
jean-marie castellucci
4,954 Points

Ribbit/Swift clone : Reusable UITableViewCells change after I scroll

I am facing a problem when using reusable UITableViewCells. For a split second before the cell updates it shows the wrong info. Like in instagram i have an avatar picture for the user who posted the photo. When the tableView is first loaded, no probleme, the avatar is the good one. If i scroll down, there are others posts with other avatars, and when i scroll up, for a split second before the cell updates it shows the wrongs avatars (from the lower cells...) Does anyone know the best practice to deal with that? In instagram we can see that avatars user are placed whithin a custom section so that the photo scroll under the section and if the scroll continue, the section is passed to another, anyone know how to do that ?

This is my code

          import UIKit
     import QuartzCore

     class WallTableViewController: UITableViewController, UINavigationControllerDelegate {

    @IBOutlet var posterAvatar: UIImageView!

override func preferredStatusBarStyle() -> UIStatusBarStyle {
    return UIStatusBarStyle.LightContent
}


var timeLineData:NSMutableArray = NSMutableArray ()
var followedFriends:NSMutableArray = NSMutableArray ()

func loadUser () {

    followedFriends.removeAllObjects()
    var friendsRelation: AnyObject! = PFUser.currentUser().objectForKey("KfriendsRelation")


    var findUser : PFQuery = friendsRelation.query()


    findUser.findObjectsInBackgroundWithBlock { (objects:[AnyObject]!, error:NSError!) -> Void in
        if !(error != nil) {
            // The find succeeded.
            println("succesfull load Users")
            // Do something with the found objects
            for object  in objects  {
                self.followedFriends.addObject(object)
                println("users added to userlist")

                for item in self.followedFriends {
                    println(item)                    }
            }
            self.tableView.reloadData()
        } else {
            // Log details of the failure
            println("error loadind user ")

        }

    }
}


func loadPost () {

   timeLineData.removeAllObjects()

    let currentUser = PFUser.currentUser()

        var findPost:PFQuery = PFQuery(className: "UserPost")


    //query for the friends of the user
    var friendsRelation: AnyObject! = PFUser.currentUser().objectForKey("KfriendsRelation")
    var findFriends : PFQuery = friendsRelation.query()

    //using the friends from the query above, we find all the posts of the friends of the current user
    var findPosts:PFQuery = PFQuery(className: "UserPost")
    findPosts.whereKey("from", matchesQuery:findFriends)
    findPost.orderByDescending("createdAt")

    //run the query
    findPosts.findObjectsInBackgroundWithBlock { (objects:[AnyObject]!, error:NSError!) -> Void in
        if !(error != nil) {
            //found posts
            for object  in objects  {
                self.timeLineData.addObject(object)
            }
            self.tableView.reloadData()
        } else {
            // Log details of the failure
            println("error loadind posts ")

        }

    }
    }
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
    return 1
}

 override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return timeLineData.count
}

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

    let cell:WallTableViewCell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as WallTableViewCell

    let userPost:PFObject = self.timeLineData.objectAtIndex(indexPath.row) as PFObject

    //define the username

    var findUser:PFQuery = PFUser.query()
    findUser.whereKey("objectId", equalTo: userPost.objectForKey("from").objectId)

    findUser.findObjectsInBackgroundWithBlock{
        (objects:[AnyObject]!, error:NSError!) -> Void in

        if error == nil {
            if let user:PFUser = (objects as NSArray).lastObject as? PFUser {
                cell.usernameLabel.text = user.username

                // define avatar poster

                if let avatarImage:PFFile = user["profileImage"] as? PFFile {
                    avatarImage.getDataInBackgroundWithBlock{(imageData:NSData!, error:NSError!)-> Void in

                        if !(error != nil) {

                            let image:UIImage = UIImage(data: imageData)


                            cell.posterAvatar.image = image as UIImage
                            cell.posterAvatar.layer.cornerRadius = 24
                            cell.posterAvatar.clipsToBounds = true

                        }

                    }
                }
                else {
                    cell.posterAvatar.image = UIImage(named: "Avatar-1")
                    cell.posterAvatar.layer.cornerRadius = 24
                    cell.posterAvatar.clipsToBounds = true
                }
            }

        }

    }

    //define the imagepost

    let imagesPost:PFFile = userPost["imageFile"] as PFFile


    imagesPost.getDataInBackgroundWithBlock{(imageData:NSData!, error:NSError!)-> Void in

        if !(error != nil) {

            let image:UIImage = UIImage(data: imageData)



            cell.imagePosted.image = image as UIImage


        }
        else {
            println("error")
        }
    }

    return cell
}


override func viewDidAppear(animated: Bool) {
    var currentUser = PFUser.currentUser()
    if (currentUser != nil) {
        println("User allready logued")
    }

    else {
        // Show the signup or login screen

        self.performSegueWithIdentifier("goToLogIn", sender: self)
    }

}

override func viewDidLoad() {
   super.viewDidLoad()


}


override func viewWillAppear(animated: Bool) {
    self.tableView.separatorColor = UIColor.whiteColor()

    tabBarController?.tabBar.tintColor = UIColor.whiteColor()
    self.loadUser()
    self.loadPost()


}

}

3 Answers

jean-marie castellucci
jean-marie castellucci
4,954 Points

OMG i've found a quickest solution thanks to Parse !

i just added this to my query :

myquery.cachePolicy = kPFCachePolicyCacheElseNetwork

And now it work perfectly.

The things that i don't know is if a particular user change his avatars photo, how do the cache will know there is something knew ?

Stone Preston
Stone Preston
42,016 Points

you need to cache the photos after you download them. Whenever you set up the cell in cellForRowAtIndexPath, you first check to see if the photo exists in your cache, if it does you dont download it you just load it from the cache instantly. If it is not in the cache, that means you have not downloaded it, so you download it from your backend, then cache it for next time.

there may be a few 3rd party swift libraries for quick and easy caching, a quick search found this one. looks simple and easy to use.

jean-marie castellucci
jean-marie castellucci
4,954 Points

Thanks Stone, isn't this one for caching NSString object only ?i ll try but i think i' m too new in dev for using this caching method. I m gona look if i find a tuto for that specific task.