1

As my title states, the pictures in my tableView shift around and are not being displayed on the correct posts when scrolling through the table view. After I stop scrolling they seem to be back into place.

I've been trying to make a sense out of the following articles:

new Firebase retrieve data and put on the tableview swift

retrieve image from Firebase storage to show on tableview swift

swift Firebase sort posts in tableview by date

But I cannot figure out how to make the pictures to display better. Here's what I have:

    import UIKit
    import FirebaseAuth
    import FirebaseDatabase
    import FirebaseStorage

    class MainFeedTableViewController: UITableViewController {

        var posts = [Post]()

        let alert = AlertsViewController()

        var databaseRef: FIRDatabaseReference! {
            return FIRDatabase.database().reference()
        }

        var storageRef: FIRStorage! {
            return FIRStorage.storage()
        }

    override func viewDidLoad() {
        super.viewDidLoad()

        fetchPosts()

    }

    // populates the tableView with posts content in real time
    private func fetchPosts(){

        let postRefs = databaseRef.child("posts")

        postRefs.observe(.value) { (snapshot: FIRDataSnapshot) in
            var newPost = [Post]()

            for post in snapshot.children{
                let postObject = Post(snapshot: post as! FIRDataSnapshot)
                newPost.insert(postObject, at: 0)
            }

            self.posts = newPost
            self.tableView.reloadData()
        }

    }

    // MARK: - Table view data source

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

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

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        let postsAtIndexPath = posts[indexPath.row]

        if postsAtIndexPath.postWithImage == true {

            let cell = tableView.dequeueReusableCell(withIdentifier: "postWithImage", for: indexPath) as! PostWithImageTableViewCell

            let postUser = postsAtIndexPath.uid

            let userRef = databaseRef.child("users/\(postUser!)")

            userRef.observe(.value, with: { (snapshot) in

                let user = User(snapshot: snapshot)

                DispatchQueue.main.async(execute: {
                    cell.userRealNameLabel.text = user.name
                    cell.usernameLabel.text = "@" + user.username
                    cell.postTextView.text = postsAtIndexPath.postText
                    cell.timeStampLabel.text = postsAtIndexPath.date
                })

                self.storageRef.reference(forURL: user.photoURL).data(withMaxSize: 1 * 1024 * 1024, completion: { (data, error) in

                    if error == nil{

                        DispatchQueue.main.async(execute: {
                            if let data = data{
                                cell.userProfilePicture.image = UIImage(data: data)
                            }
                        })

                    }
                    else{

                        print(error!.localizedDescription)
                    }
                })

                self.storageRef.reference(forURL: postsAtIndexPath.postPictureURL).data(withMaxSize: 1 * 1024 * 1024, completion: { (data, error) in

                    if error == nil{

                        DispatchQueue.main.async(execute: {
                            if let data = data{
                                cell.postImage.image = UIImage(data: data)

                            }
                        })

                    }
                    else{
                        self.alert.displayAlert(alertTitle: "Error", alertMessage: error!.localizedDescription, fromController: self)
                    }
                })

            }) { (error) in
                self.alert.displayAlert(alertTitle: "Error", alertMessage: error.localizedDescription, fromController: self)
            }

            return cell
        }
        else{

            let cell = tableView.dequeueReusableCell(withIdentifier: "postWithText", for: indexPath) as! PostTableViewCell

            let postUser = postsAtIndexPath.uid

            let userRef = databaseRef.child("users/\(postUser!)")

            userRef.observe(.value, with: { (snapshot) in

                let user = User(snapshot: snapshot)

                DispatchQueue.main.async(execute: {
                    cell.userRealNameLabel.text = user.name
                    cell.usernameLabel.text = "@" + user.username
                    cell.postTextView.text = postsAtIndexPath.postText
                    cell.timestampLabel.text = postsAtIndexPath.date

                })

                self.storageRef.reference(forURL: user.photoURL).data(withMaxSize: 1 * 1024 * 1024, completion: { (data, error) in

                    if error == nil{

                        DispatchQueue.main.async(execute: {
                            if let data = data{
                                cell.userProfilePicture.image = UIImage(data: data)
                            }
                        })

                    }
                    else{

                        print(error!.localizedDescription)
                    }
                })

            }) { (error) in
                self.alert.displayAlert(alertTitle: "Error", alertMessage: error.localizedDescription, fromController: self)
            }

            return cell
        }

    }

}

The reason I'm having a hard time, it's because I want the user's username, name, and profile picture to change everywhere and in real time when they edit their info. That's why I'm retrieving the user info based on the post's user uid.

What's a better way to implement this?

Community
  • 1
  • 1
Bruno Recillas
  • 951
  • 1
  • 9
  • 18

1 Answers1

5

You have to override the prepareForReuse function in PostWithImageTableViewCell.

EXAMPLE

 override func prepareForReuse() {
        super.prepareForReuse()
        self.userProfilePicture.image = nil
        //reset the rest of the values on your `UITableViewCell` subclass
 }

EDIT

Since the reuse issue has been resolved, i would like to recommend the following caching framework, Kingfisher, for more convenient image display experience.

Kingfisher has a great extension on UIImageView, what will take care of the caching for your.

Here how you would use it:

let url = URL(string: "https://domain.com/image.jpg")!
imageView.kf.setImage(with: url)

You only set to the URL, what will uniquely identify the image resource, and downloads only once. Here is a cheat sheet, how to use it.

Jordi Bruin
  • 1,588
  • 1
  • 11
  • 20
dirtydanee
  • 6,081
  • 2
  • 27
  • 43