I'm trying to program an expandable UICollectionViewCell. If the user presses the topbutton, the cell should expand to a different height and reveal the underlying content.
My first implementation features a custom UICollectionViewCell, which can be expanded by clicking the topbutton, but the collectionview does not expand too. Atm. the cell is the last one in the collectionview and just by swiping down enough, you can see the expanded content. If you stop touching, the scrollview jumps up and nothing of the expanded cell could be seen.
Here's my code:
import Foundation
import UIKit
class ExpandableCell: UICollectionViewCell {
@IBOutlet weak var topButton: IconButton!
public var expanded : Bool = false
private var expandedHeight : CGFloat = 200
private var notExpandedHeight : CGFloat = 50
@IBAction func topButtonTouched(_ sender: AnyObject) {
if (expanded == true) {
self.deExpand()
} else {
self.expand()
}
}
public func expand() {
UIView.animate(withDuration: 0.8, delay: 0.0, usingSpringWithDamping: 0.9, initialSpringVelocity: 0.9, options: UIViewAnimationOptions.curveEaseInOut, animations: {
self.frame = CGRect(x: self.frame.origin.x, y: self.frame.origin.y, width: self.frame.size.width, height: self.expandedHeight)
}, completion: { success in
self.expanded = true
})
}
public func deExpand() {
UIView.animate(withDuration: 0.8, delay: 0.0, usingSpringWithDamping: 0.9, initialSpringVelocity: 0.9, options: UIViewAnimationOptions.curveEaseInOut, animations: {
self.frame = CGRect(x: self.frame.origin.x, y: self.frame.origin.y, width: self.frame.size.width, height: self.notExpandedHeight)
}, completion: { success in
self.expanded = false
})
}
}
I also have problems implementing the following method correctly, because I haven't got a direct reference to the cell:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize
Basicly everything should be animated.
The screenshot shows an expanded and not expanded cell. Hope that helps!
Thanks!
If you have more than cell that needs to be expanded to different height when a button is touched inside the collection view cell, then, here is the code.
I have used the delegate pattern to let the controller know, button of which cell was touched using the indexPath. You need to pass the indexpath of the cell to the cell when creating the cell.
when the button is touched the cell calls the delegate(ViewController), which updates the isExpandedArray accordingly and reloads the particular cell.
CollectionViewCell
protocol ExpandedCellDelegate:NSObjectProtocol{
func topButtonTouched(indexPath:IndexPath)
}
class ExpandableCell: UICollectionViewCell {
@IBOutlet weak var topButton: UIButton!
weak var delegate:ExpandedCellDelegate?
public var indexPath:IndexPath!
@IBAction func topButtonTouched(_ sender: UIButton) {
if let delegate = self.delegate{
delegate.topButtonTouched(indexPath: indexPath)
}
}
}
View Controller Class
class ViewController: UIViewController {
@IBOutlet weak var collectionView: UICollectionView!
var expandedCellIdentifier = "ExpandableCell"
var cellWidth:CGFloat{
return collectionView.frame.size.width
}
var expandedHeight : CGFloat = 200
var notExpandedHeight : CGFloat = 50
var dataSource = ["data0","data1","data2","data3","data4"]
var isExpanded = [Bool]()
override func viewDidLoad() {
super.viewDidLoad()
isExpanded = Array(repeating: false, count: dataSource.count)
}
}
extension ViewController:UICollectionViewDataSource{
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return dataSource.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
//configure Cell
return cell
}
}
extension ViewController:UICollectionViewDelegateFlowLayout{
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
if isExpanded[indexPath.row] == true{
return CGSize(width: cellWidth, height: expandedHeight)
}else{
return CGSize(width: cellWidth, height: notExpandedHeight)
}
}
}
extension ViewController:ExpandedCellDelegate{
func topButtonTouched(indexPath: IndexPath) {
isExpanded[indexPath.row] = !isExpanded[indexPath.row]
UIView.animate(withDuration: 0.8, delay: 0.0, usingSpringWithDamping: 0.9, initialSpringVelocity: 0.9, options: UIViewAnimationOptions.curveEaseInOut, animations: {
self.collectionView.reloadItems(at: [indexPath])
}, completion: { success in
print("success")
})
}
}
Don't change the cell frame directly. Implement func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize
and handle heights there.
Lets say you have one cell in your collectionView and a top button above it:
let expanded = false
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize{
if expanded{
// what is the size of the expanded cell?
return collectionView.frame.size
}else{
// what is the size of the collapsed cell?
return CGSizeZero
}
}
@IBAction func didTouchUpInsideTopButton(sender: AnyObject){
// top button tap handler
self.expanded = !self.expanded
// this call will reload the cell and make it redraw (animated) Since the expanded flag changed, the sizeForItemAt call above will return a different size and animate the size change
self.collectionView.reloadItemsAtIndexPaths([NSIndexPath(forRow: 0, inSection: 0)])
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With