Videos are placed on top of images in collection view? - ios

I have a collection view which is populated with videos or images, based on what is fetched from firebase.
I am having this problem where when I fetch the data and add to the cells, some cells get a video added on top [of the image].
I have looked through the code found bellow and cant seem to . find where the problem is coming from.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "p1Item", for: indexPath) as! CollectionViewCell1
if postArray[indexPath.item].media[0].image != nil { //its an image
let date = ConvertDate(mediaTimestamp: postArray[indexPath.row].media[0].postNum!, isItForP3: false).getDate!
do {
cell.imageView.image = postArray[indexPath.item].media[0].image!
if postArray[indexPath.item].user.profileImageUrl != "" {
let url = URL(string: "\(postArray[indexPath.item].user.profileImageUrl)")
let data = try Data(contentsOf: url!)
if let profileImage = UIImage(data: data) {
cell.profImage.image = profileImage
cell.titleLabel.text = postArray[indexPath.item].user.username
cell.subtitleLabel.text = date
}
} else {
//I add a fake image here
cell.titleLabel.text = postArray[indexPath.item].user.username
cell.subtitleLabel.text = date
cell.profImage.image = UIImage(named: "media")
}
} catch {
print("need to add stuff to this catch")
}
} else { //its a video
let videoURL = postArray[indexPath.item].media[0].videoURL
let player = AVPlayer(url: videoURL! as URL)
let playerLayer = AVPlayerLayer(player: player)
playerLayer.frame = cell.bounds
cell.layer.addSublayer(playerLayer)
let date = ConvertDate(mediaTimestamp: postArray[indexPath.item].media[0].postNum!, isItForP3: false).getDate!
do {
if postArray[indexPath.item].user.profileImageUrl != "" {
let url = URL(string: "\(postArray[indexPath.item].user.profileImageUrl)")
let data = try Data(contentsOf: url!)
if let profileImage = UIImage(data: data) {
cell.profImage.image = profileImage
cell.titleLabel.text = postArray[indexPath.item].user.username
cell.subtitleLabel.text = date
}
} else {
//I add a fake profile image here
cell.titleLabel.text = postArray[indexPath.item].user.username
cell.subtitleLabel.text = date
cell.profImage.image = UIImage(named: "media")
}
} catch {
print("need to add stuff to this catch")
}
}
print("Completed: ", cell.imageView!)
return cell
}
How do i fix this?

The reason is here
cell.layer.addSublayer(playerLayer)
you should clear the cell because it's dequeued
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "p1Item", for: indexPath) as! CollectionViewCell1
cell.layer.sublayers.forEach {
if $0 is AVPlayerLayer {
$0.removeFromSuperlayer()
}
}
, also you shouldn't use
let data = try Data(contentsOf: url!)
for remote data gribbing , you may use SDWebImage

When you say
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "p1Item", for: indexPath) as! CollectionViewCell1
This means that you are reusing the old cell and in your case the old cell might have a AVPlayerLayer
One way to fix this is to use different withReuseIdentifier
if postArray[indexPath.item].media[0].image != nil { //its an image
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ImgageCell", for: indexPath) as! CollectionViewCell1
return cell
} else { //its a video
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "VideoCell", for: indexPath) as! CollectionViewCell1
return cell
}
}
Or reset the Cell before reuse i.e. remove the layer after dequeueReusableCell

Related

How can I improve my cellForRowAt function?

I have read that the cellForRowAt method should not be to resource-intensive as it is called frequently while a user is scrolling through a TableView I tried to limit how much went into my cellForRowAt but seem to have overused it because anytime it is called scrolling is interrupted as it loads more data.
Here is my function:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
print(indexPath)
var post: PostStruct
var peopleUserIsFollowing: [String] = []
let cell = tableView.dequeueReusableCell(withIdentifier: K.cellIdentifier, for: indexPath) as! PostCell
cell.delegate = self
if postArray.count == 0 {
let instructions = cell.textLabel
instructions?.text = "Press the camera to start Piking!"
instructions?.textAlignment = .center
clearPosts(cell)
}else {
post = postArray[indexPath.row]
if let leftPostArray = userDefaults.array(forKey: fbLeftKey) as? [String]{
votedLeftPosts = leftPostArray
}
if let rightPostArray = userDefaults.array(forKey: fbRightKey) as? [String]{
votedRightPosts = rightPostArray
}
let firstReference = storageRef.child(post.firstImageUrl)
let secondReference = storageRef.child(post.secondImageUrl)
cell.firstImageView.sd_setImage(with: firstReference)
cell.secondImageView.sd_setImage(with: secondReference)
//For FriendsTableView query
let db = Firestore.firestore()
let followingReference = db.collection("following")
.document(currentUser!)
.collection("UserIsFollowing")
followingReference.getDocuments(){(querySnapshot, err) in
if let err = err {
print("Error getting documents: \(err)")
} else {
for document in querySnapshot!.documents {
peopleUserIsFollowing.append(document.documentID)
}
}
}
//Fill in labels and imageViews
cell.timer = createTimer(post, cell)
cell.leftTitle.text = post.firstTitle
cell.rightTitle.text = post.secondTitle
cell.postDescription.text = post.postDescription
if post.userPic == "" {
userPic =
"Placeholder"
} else{
userPic = post.userPic
}
let url = URL(string: userPic)
let data = try? Data(contentsOf: url!) //make sure your image in this url does exist, otherwise unwrap in a if let check / try-catch
cell.profilePic.image = UIImage(data: data!)
let votesCollection = db.collection("votes").document(post.postID)
getCount(ref: votesCollection, cell: cell)
if(post.uid != currentUser){
cell.userName.text = post.poster
}else{
cell.userName.text = "Me"
cell.tapLeft.isEnabled = false
cell.tapRight.isEnabled = false
}
cell.textLabel?.text = ""
if(post.poster == Auth.auth().currentUser!.uid || post.endDate - Int(NSDate().timeIntervalSince1970) <= 0){
cell.tapRight.isEnabled = false
cell.tapLeft.isEnabled = false
cell.firstImageView.layer.borderWidth = 0
cell.secondImageView.layer.borderWidth = 0
}
else if(votedRightPosts.contains(post.firstImageUrl)){
cell.secondImageView.layer.borderColor = UIColor.green.cgColor
cell.secondImageView.layer.borderWidth = 4
cell.firstImageView.layer.borderWidth = 0
cell.tapRight.isEnabled = false
cell.tapLeft.isEnabled = true
}
else if (votedLeftPosts.contains(post.firstImageUrl)){
cell.firstImageView.layer.borderColor = UIColor.green.cgColor
cell.firstImageView.layer.borderWidth = 4
cell.secondImageView.layer.borderWidth = 0
cell.tapLeft.isEnabled = false
cell.tapRight.isEnabled = true
}
}
return cell
}
I felt that the biggest issue would probably be the three images that are loaded into the cell but I am utilizing caching and when I commented out their loading I experienced the same issue. I have also read that utilizing a model can help which I believe that I did with the PostStruct object. I think I may need to pre-fetch however I cannot figure out where to even start with that. How can I improve my function so that scrolling in my app is smoother?
This would be a great place to use the Instruments tool. Run the time profiler on your app as you scroll through your table view. It will show you where the bulk of the delay is coming from.
Where are your images coming from? Are they loading from your local file system, or are those URLs network URLS?
You might need to refactor your code to return cells without the images, and trigger async code that loads the images in a background thread, installs them into your model, and then updates the cell once the images have finished loading.
I found out that this line of code was the issue:
if post.userPic == "" {
userPic =
"Placeholder"
} else{
userPic = post.userPic
}
let url = URL(string: userPic)
let data = try? Data(contentsOf: url!) //make sure your image in this url does exist, otherwise unwrap in a if let check / try-catch
cell.profilePic.image = UIImage(data: data!)
In stead I changed it to utilize SDWebImage like the other two images in the post.

How to stop tableview cells from glitching and flickering image when scrolling in swift?

I have a tableview that displays data from Firebase. It displays fine in the tableview but when scrolling begins, the table starts glitching or having the cells image and text reload and flicker which is very ugly. Help! Here's my code:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "MainTableViewCell", for: indexPath) as! MainTableViewCell
func downloadPicture(finished: () -> Void) {
cell.profilePicture.image = nil
if let imageUrlString = self.payments[indexPath.row].picture,
let imageUrl = URL(string: imageUrlString) {
do {
let imageData = try Data(contentsOf: imageUrl)
cell.profilePicture.image = UIImage(data: imageData)
cell.profilePicture.layer.cornerRadius = cell.profilePicture.frame.size.width / 2
cell.profilePicture.clipsToBounds = true
cell.profilePicture.alpha = 1
}
catch {
print("Error fetching image - \(error)")
}
}
finished()
}
downloadPicture {
print("success")
}
cell.amountLabel.text = "$\(self.payments[indexPath.row].amount ?? "")"
cell.detailsLabel.text = self.payments[indexPath.row].amount ?? ""
return cell
}
You can simply go with SDWebImage An asynchronous image downloader with cache management and efficient to use.
i.e. :
import SDWebImage
yourImageView.sd_setImage(with: URL(string: "yourImageURLFromFirebase.jpg"), placeholderImage: UIImage(named: "placeholder.png"))
#Lukeksaunders just go to GitHub Kinfisher. This library contains all functionality you want.
import Kingfisher
let url = URL(string: "https://example.com/image.png")
imageView.kf.setImage(with: url)
This library cache your images
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "MainTableViewCell", for: indexPath) as! MainTableViewCell
if let imageUrlString = self.payments[indexPath.row].picture,
let imageUrl = URL(string: imageUrlString) {
cell.profilePicture.kf.setImage(with: imageUrl)
}
cell.amountLabel.text = "$\(self.payments[indexPath.row].amount ?? "")"
cell.detailsLabel.text = self.payments[indexPath.row].amount ?? ""
return cell
}

Image not loading in collection view cell

I've programmatically setup a collection view that should display an image from an API onto a cell. However, once the cells are displayed and data is called the cells remain empty.
Cells returning an empty image view
Current setup
override func viewDidLoad() {
super.viewDidLoad()
collectionView.register(ImageCell.self, forCellWithReuseIdentifier: imageCellId)
loadImageData(numberOfItems: numberOfItems)
}
func loadImageData(numberOfItems: Int) {
client.getImageData(items: numberOfItems, completion: { (error,data) in
if error != nil {
print("Error parsing image data")
} else {
self.per_page = data.perPage
self.total_results = data.totalResults
self.images = data.photos
for image in self.images {
self.userNameArray.append(image.photographer)
self.urlArray.append(image.url)
}
self.imageLinkArray = self.urlArray
DispatchQueue.main.async {
self.collectionView.reloadData()
}
}
})
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: imageCellId, for: indexPath) as! ImageCell
let imageString = String(imageLinkArray[indexPath.row])
let url = URL(string: imageString)
let data = try? Data(contentsOf: url!)
if let imageData = data {
let imageFromDatabase = UIImage(data: imageData)
cell.imageView.image = imageFromDatabase
}
return cell
}
Tried:
Made sure the URLs are coming back using a print statement for the url constant in cellForItemAt.
I've also tested out the cell layout by using a placeholder image.
Called collectionView.reloadData() in viewDidAppear().
Collection View Cells not appearing
Images not displayed in collection view cell
You should use Kingfisher as an image caching library
https://github.com/onevcat/Kingfisher
First, add the pod to your project:
pod 'Kingfisher'
Replace this:
let url = URL(string: imageString)
let data = try? Data(contentsOf: url!)
if let imageData = data {
let imageFromDatabase = UIImage(data: imageData)
cell.imageView.image = imageFromDatabase
}
with this
let url = URL(string: imageString)
imageView.kf.setImage(with: url)

Image in table view [duplicate]

This question already has an answer here:
Asynchronously load image in uitableviewcell
(1 answer)
Closed 5 years ago.
I have a problem with the image in table view cell
The images are downloaded form the Google Firebase and in every cell there is one of that
But when I scroll up or down the images change automatically the index
Here is my code, someone can help me? thanks a lot!
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if postsArray[indexPath.row].imageNoImage == true{
let cell = tableView.dequeueReusableCell(withIdentifier: "imageLook", for: indexPath) as! imageLookTableViewCell
cell.authorLabel.text = self.postsArray[indexPath.row].author
cell.likesCountLabel.text = "\(self.postsArray[indexPath.row].likes!)"
cell.postID = postsArray[indexPath.row].postId
cell.textViewPost.text = self.postsArray[indexPath.row].textPost
let url = URL(string: postsArray[indexPath.row].pathToImage as! String)
if url != nil {
DispatchQueue.global().async {
let data = try? Data(contentsOf: url!)
DispatchQueue.main.async {
if data != nil {
cell.imagePost.image = UIImage(data:data!)
}else{
}
}
}
}
for person in self.postsArray[indexPath.row].peopleWhoLike {
if person == FIRAuth.auth()!.currentUser!.uid {
cell.likesBtn.isHidden = false
break
}
}
return cell
}
Your question is not very descriptive/well written, but I think your problem is that you are not caching your images.
Try this:
let imageCache = NSCache()
let cell = tableView.dequeueReusableCell(withIdentifier: "imageLook", for: indexPath) as! imageLookTableViewCell
cell.authorLabel.text = self.postsArray[indexPath.row].author
cell.likesCountLabel.text = "\(self.postsArray[indexPath.row].likes!)"
cell.postID = postsArray[indexPath.row].postId
cell.textViewPost.text = self.postsArray[indexPath.row].textPost
let url = URL(string: postsArray[indexPath.row].pathToImage as! String)
if url != nil {
if let cachedImage = imageCache.objectForKey(url) as? UIImage {
cell.imagePost.image = cachedImage
return
}
DispatchQueue.global().async {
let data = try? Data(contentsOf: url!)
DispatchQueue.main.async {
if data != nil {
if let myImageName = UIImage(data:data!){
imageCache.setObject(myImageName, forKey: url)
}
cell.imagePost.image = UIImage(data:data!)
}else{
}
}
}
}
for person in self.postsArray[indexPath.row].peopleWhoLike {
if person == FIRAuth.auth()!.currentUser!.uid {
cell.likesBtn.isHidden = false
break
}
}
return cell
}

GCD Returning Incorrect Results in UITableView

I have a list of about 8,500 items that load in a UITableView and there should be only about 200 items in which product.isnewitem is true. For every 'new' item, an image (newicon.png) is supposed to load indicating that it is a new item; however, when I start scrolling down on the table view, newicon shows up on over 50% of the items. All the items are loaded via Realm.
The check for new items is done in:
if product.isnewitem {
cell.newIconImageView.image = #imageLiteral(resourceName: "newicon.png")
}
Here is the entire cellForRowAtIndexPath method:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let paths = NSSearchPathForDirectoriesInDomains(documentsDirectory, userDomainMask, true)
let cell = tableView.dequeueReusableCell(withIdentifier: "ProductCell") as? OrderFormViewCell
?? UITableViewCell(style: .subtitle, reuseIdentifier: "ProductCell") as! OrderFormViewCell
let realm = try! Realm()
let allProducts = realm.objects(Product.self).sorted(byKeyPath: "basedescription")
let product = allProducts[indexPath.row]
cell.productDescriptionLabel.text = product.basedescription
queue.async {
let realm = try! Realm()
let allProducts = realm.objects(Product.self).sorted(byKeyPath: "basedescription")
let product = allProducts[indexPath.row]
if product.isnewitem {
cell.newIconImageView.image = #imageLiteral(resourceName: "newicon.png")
}
if let dirPath = paths.first {
let imageURL = URL(fileURLWithPath: dirPath).appendingPathComponent("T\(product.itemno.replacingOccurrences(of: "-", with: "")).png")
if let image = UIImage(contentsOfFile: imageURL.path) {
cell.productImageView.image = image
}
else {
cell.productImageView.image = #imageLiteral(resourceName: "image-coming-soon.png")
}
}
}
return cell
}
I can’t understand why you use this code
let realm = try! Realm()
let allProducts = realm.objects(Product.self).sorted(byKeyPath: "basedescription")
let product = allProducts[indexPath.row]
two times in your cellForRowAtIndexPath and one in a queue, I think you must move this code to your viewController viewDidLoad, or viewWillAppear and then use the products from a local array declared on your viewController
var allProducts : Results<Product>?
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
let realm = try! Realm()
self.allProducts = realm.objects(Product.self).sorted(byKeyPath: "basedescription")
}
and in your cellForRowAtIndexPath you should have something like this
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let paths = NSSearchPathForDirectoriesInDomains(documentsDirectory, userDomainMask, true)
let cell = tableView.dequeueReusableCell(withIdentifier: "ProductCell") as? OrderFormViewCell
?? UITableViewCell(style: .subtitle, reuseIdentifier: "ProductCell") as! OrderFormViewCell
let product = self.allProducts[indexPath.row]
cell.productDescriptionLabel.text = product.basedescription
if product.isnewitem {
cell.newIconImageView.image = #imageLiteral(resourceName: "newicon.png")
}
else {
cell.newIconImageView.image = nil
}
if let dirPath = paths.first {
let imageURL = URL(fileURLWithPath: dirPath).appendingPathComponent("T\(product.itemno.replacingOccurrences(of: "-", with: "")).png")
if let image = UIImage(contentsOfFile: imageURL.path) {
cell.productImageView.image = image
}
else {
cell.productImageView.image = #imageLiteral(resourceName: "image-coming-soon.png")
}
}
return cell
}
I hope this helps you

Resources