I have a view controller that contains a uicollectionview
. Each collectionview cell contains a button that, when clicked, adds a new label within the cell. To expand the height of each cell I call reloadItems(at: [indexPath])
.
Unfortunately calling reloadItems(at: [indexPath])
fades out the old label and fades in the new label, how do I prevent any labels from fading out?
The bug becomes even more apparent every time I click the addLabel
button: a new label fades in but whatever previous labels had not been visible suddenly appear again and whatever labels used to be visible, magically turn invisible again.
reloadItems(at: [indexPath])
seems to toggle the alpha of each new label differently. I would like to resize and add new labels to the cell without having any labels disappear.
Here is my code:
ViewController
class ViewController: UIViewController {
weak var collectionView: UICollectionView!
var expandedCellIdentifier = "ExpandableCell"
var cellWidth:CGFloat{
return collectionView.frame.size.width
}
var expandedHeight : CGFloat = 200
var notExpandedHeight : CGFloat = 50
//the first Int gives the row, the second Int gives the amount of labels in the row
var isExpanded = [Int:Int]()
override func viewDidLoad() {
super.viewDidLoad()
for i in 0..<4 {
isExpanded[i] = 1
}
}
}
extension ViewController:UICollectionViewDataSource{
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return isExpanded.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: expandedCellIdentifier, for: indexPath) as! ExpandableCell
cell.indexPath = indexPath
cell.delegate = self
cell.setupCell = "true"
return cell
}
}
extension ViewController:UICollectionViewDelegateFlowLayout{
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
if isExpanded[indexPath.row]! > 1{
let height = (collectionView.frame.width/10)
let newHeight = height * CGFloat(isExpanded[indexPath.row]!)
return CGSize(width: cellWidth, height: newHeight)
}else{
return CGSize(width: cellWidth, height: collectionView.frame.width/6 )
}
}
}
extension ViewController:ExpandedCellDeleg{
func topButtonTouched(indexPath: IndexPath) {
isExpanded[indexPath.row] = isExpanded[indexPath.row]! + 1
UIView.animate(withDuration: 0.8, delay: 0.0, usingSpringWithDamping: 0.9, initialSpringVelocity: 0.9, options: UIView.AnimationOptions.curveEaseInOut, animations: {
self.collectionView.reloadItems(at: [indexPath])
}, completion: { success in
print("success")
})
}
}
Protocol
protocol ExpandedCellDeleg:NSObjectProtocol{
func topButtonTouched(indexPath:IndexPath)
}
ExpandableCell
class ExpandableCell: UICollectionViewCell {
weak var delegate:ExpandedCellDeleg?
public var amountOfIntervals:Int = 1
public var indexPath:IndexPath!
var setupCell: String? {
didSet {
print("cell should be setup!!")
}
}
let ivAddLabel: UIImageView = {
let imageView = UIImageView()
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.image = #imageLiteral(resourceName: "plus")
imageView.tintColor = .black
imageView.contentMode = .scaleToFill
imageView.backgroundColor = UIColor.clear
return imageView
}()
override init(frame: CGRect) {
super.init(frame: .zero)
contentView.addSubview(ivAddLabel)
let name = UILabel(frame: CGRect(x: 0, y: 0, width: 100, height: 18))
name.center = CGPoint(x: Int(frame.width)/2 , y: 20)
name.textAlignment = .center
name.font = UIFont.systemFont(ofSize: 16)
name.textColor = UIColor.black
name.text = "Fred"
contentView.addSubview(name)
ivAddLabel.rightAnchor.constraint(equalTo: self.rightAnchor, constant: -14).isActive = true
ivAddLabel.topAnchor.constraint(equalTo: self.topAnchor, constant: 10).isActive = true
ivAddLabel.widthAnchor.constraint(equalToConstant: 20).isActive = true
ivAddLabel.heightAnchor.constraint(equalToConstant: 20).isActive = true
ivAddLabel.layer.masksToBounds = true
ivAddLabel.isUserInteractionEnabled = true
let addGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(ivAddLabelSelected))
ivAddLabel.addGestureRecognizer(addGestureRecognizer)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
@objc func ivAddLabelSelected(){
print("add button was tapped!")
if let delegate = self.delegate{
amountOfIntervals = amountOfIntervals + 1
let height = (20*amountOfIntervals)
let name = UILabel(frame: CGRect(x: 0, y: 0, width: 100, height: 18))
name.center = CGPoint(x: Int(frame.width)/2, y: height)
name.textAlignment = .center
name.font = UIFont.systemFont(ofSize: 16)
name.textColor = UIColor.black
name.text = "newFred"
name.alpha = 0.0
contentView.addSubview(name)
UIView.animate(withDuration: 0.2, animations: { name.alpha = 1.0 })
delegate.topButtonTouched(indexPath: indexPath)
}
}
}
It's because you animate the new label
UIView.animate(withDuration: 0.2, animations: { name.alpha = 1.0 })
and in parallel reload the cell which creates a new cell/reuses existing and shows it, but also you wrap the reload into animation block which seems strange and useless:
UIView.animate(withDuration: 0.8, delay: 0.0, usingSpringWithDamping: 0.9, initialSpringVelocity: 0.9, options: UIView.AnimationOptions.curveEaseInOut, animations: {
self.collectionView.reloadItems(at: [indexPath])
}, completion: { success in
print("success")
})
You need to remove both animations and just reload the cell. If you need a nice animation of cell expansion you need to implement collection layout which will handle all states - start, intermediate, end of the animation. It's hard.
Try to use suggested in other answer "UICollectionView Self Sizing Cells with Auto Layout" if it will not help, then either forgot the idea of animation or implement custom layout.