0

I have a UICollectionViewCell:

enter image description here

"Gradient View" is a UIView that has a gradient:

enter image description here

Source of data is CoreData. On Collection view, I have buttons that sort CoreData:

@IBAction func allMealsBtn(_ sender: Any) {
    let fetchRequest: NSFetchRequest<Meal> = Meal.fetchRequest()
    let dateSort = NSSortDescriptor(key: "created", ascending: false)
    fetchRequest.sortDescriptors = [dateSort]

    let controller = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil)

    controller.delegate = self
    self.controller = controller

    do {
        try controller.performFetch()
    } catch {
        let error = error as NSError
        print("Couldn't fetch data from CoreData with error: \(error.debugDescription)")
    }
    collectionView.reloadData()

}

After pressing the sort button, collection view reloads and displays the right data. However, the Gradient View multiplies, as though the cell wasn't destroyed. Here what I see after sorting data multiple times:

enter image description here

How to make sure that my cell is redrawn from scratch?

Edit:

My custom cell class. So in configureCell I have to check if my gradientView is added to the view, if yes, then add gradient, if not, add it, and add gradient. How can I do this check?

class CollectionCell: UICollectionViewCell {

@IBOutlet weak var mealImg: UIImageView!
@IBOutlet weak var mealTitleLbl: UILabel!
@IBOutlet weak var gradientView: UIView!

func configureCell(meal: Meal) {
    mealTitleLbl.text = meal.title

    let img = meal.getMealImage()
    mealImg.image = img

    gradientView.addGradientWithColor(color: UIColor.clear)

    // How can I check if my gradientView is added to the view, if yes, then add gradient, if not, add it, and only then add gradient.

}

override func prepareForReuse() {
    super.prepareForReuse()

    gradientView.removeFromSuperview()
}
}

extension UIView {
func addGradientWithColor(color: UIColor) {
    let gradient = CAGradientLayer()
    gradient.frame = self.bounds
    let topColor = UIColor(red:0.07, green:0.07, blue:0.07, alpha:1)
    gradient.colors = [topColor.cgColor, color.cgColor]

    self.layer.insertSublayer(gradient, at: 0)
}
}

Edit: I've dropped the extension and implemented the flag logic. However, after the view gets removed, it never reappears on the cell after performing the search. Any thoughts?

class CollectionCell: UICollectionViewCell {

@IBOutlet weak var mealImg: UIImageView!
@IBOutlet weak var mealTitleLbl: UILabel!
@IBOutlet weak var gradientView: UIView!

var isGradientAdded = false

func configureCell(meal: Meal) {
    mealTitleLbl.text = meal.title

    let img = meal.getMealImage()
    mealImg.image = img

    if isGradientAdded == false {
        addGradient()
        isGradientAdded = true
    }
}

override func prepareForReuse() {
    super.prepareForReuse()

    gradientView.removeFromSuperview()
}

  func addGradient () {
    let gradient = CAGradientLayer()
    gradient.frame = gradientView.bounds
    let topColor = UIColor(red:0.07, green:0.07, blue:0.07, alpha:1)
    let botomColor = UIColor.clear
    gradient.colors = [topColor.cgColor, botomColor.cgColor]
    gradientView.layer.insertSublayer(gradient, at: 0)
}
}
Mr_Vlasov
  • 515
  • 2
  • 6
  • 25
  • Try to override prepareForReuse method – Andriy Savran Jan 30 '17 at 21:50
  • @WetSweater what should be done inside of it? – Mr_Vlasov Jan 30 '17 at 21:56
  • do you use any "addSubview" methods somewhere in your cell or in the collectionView data source methods? – Denislava Shentova Jan 30 '17 at 22:05
  • This is unlikely to be the issue since the cell is always visible and `prepareForReuse` is probably not being called. This issue may stem from where the datasource asks for a cell. We need to see what your datasource/cell setup looks like. – sooper Jan 30 '17 at 22:21
  • @sooper if so I guess there is "addSubview" function somewhere ... – Evgeny Karkan Jan 30 '17 at 22:31
  • @DenislavaShentova in my custom Cell class I have a UIView extension that creates a gradient layer and inserts it as a sublayer. How can I get a hold of that sublayer within "prepareForReuse" and then delete it? – Mr_Vlasov Jan 30 '17 at 23:09
  • @Mr_Vlasov If the problem is with the addSubview.. just where you add it, check if it's already there. Keep it as property of the cell and check if it's nil -> if it is Not nil, don't call the addSubview. – Denislava Shentova Jan 30 '17 at 23:22
  • @DenislavaShentova i've added my custom cell code. The gradient view has its gradient as a sublayer. How can I perform a check for its existence? Or should I add gradient somehow different? – Mr_Vlasov Jan 30 '17 at 23:28
  • You can make a bool property isGradientAdded with default value false. Then in your configureCell method, check the value - if it's false, call the addGradientWithColor method and set isGradientAdded = true. Next time the collection view reuses the cell, the bool property with still be true, so no gradient added. – Denislava Shentova Jan 30 '17 at 23:33
  • @DenislavaShentova after I gradientView.removeFromSuperview() in prepareForReuse() my gradient gets destroyed and never appears. – Mr_Vlasov Jan 31 '17 at 00:18
  • @DenislavaShentova implemented the flag logic, but now gradient simple destroyed and doesn't reappear. Any thoughts? – Mr_Vlasov Jan 31 '17 at 01:09
  • @Mr_Vlasov did you remove this line gradientView.removeFromSuperview()? – Denislava Shentova Jan 31 '17 at 08:15
  • @DenislavaShentova I kept that line, but implemented a slightly different flag logic. You can see my solution here: http://stackoverflow.com/questions/41948576/removing-a-subview-then-adding-it-back – Mr_Vlasov Jan 31 '17 at 08:56

1 Answers1

2

In UICollectionViewCell subclass (Cell) you should override prepeareForReuse function - to turn cell to default mode.

If you don't have the custom class for Cell - create it before and assign in interface builder as class for your Cell cell (and of course don't forget about outlets)

override func prepareForReuse() {
    super.prepareForReuse()

    //set cell to initial state here 
    //e.g try to remove you gradient view  
    yourGradientView.removeFromSuperview()
}
Evgeny Karkan
  • 8,782
  • 2
  • 32
  • 38
  • Yes, I did add this code to my custom Cell class. It worked but it removed the gradientView completely. And the new cell appeared without it. – Mr_Vlasov Jan 30 '17 at 23:02
  • @Mr_Vlasov Well, so try to improve your logic where cell is instantiated and add gradient view there... in another words find appropriate place for gradient adding... – Evgeny Karkan Jan 30 '17 at 23:09
  • I've added the custom cell code. Please consider my logic and suggested improvements. – Mr_Vlasov Jan 30 '17 at 23:24
  • 1
    Bug Thanks @EvgenyKarkan You saved Me. – Kudos Jun 24 '21 at 12:42