I am trying to get the UICollectionView, and UIView above it, to scroll together. I've done a couple of diagrams (before and after scrolling) to show what I'm trying to achieve.
Before scrolling
After scrolling
Currently, I have only been able to get the UICollectionView to scroll within its own section of the screen. I've also tried wrapping both the UIView and UICollectionView into a UIScrollView, although then the UICollectionView doesn't appear on screen.
What's the best way to implement this layout?
Here is a collection view with a section header that scrolls when the collection view scrolls.. If you need a parallax header (stretchy header effect), then there are tons of tutorials online for that..
import UIKit
class Header : UICollectionReusableView {
override init(frame: CGRect) {
super.init(frame: frame)
self.backgroundColor = UIColor.green
}
#available(iOS, unavailable)
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
class ViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
private var flowLayout: UICollectionViewFlowLayout!
private var collectionView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor.white
self.flowLayout = UICollectionViewFlowLayout()
self.flowLayout.scrollDirection = .vertical;
self.flowLayout.minimumInteritemSpacing = 15;
self.flowLayout.minimumLineSpacing = 15;
self.collectionView = UICollectionView(frame: .zero, collectionViewLayout: self.flowLayout)
self.collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "default")
self.collectionView.register(UICollectionReusableView.self, forSupplementaryViewOfKind: UICollectionElementKindSectionHeader, withReuseIdentifier: "default")
self.collectionView.register(Header.self, forSupplementaryViewOfKind: UICollectionElementKindSectionHeader, withReuseIdentifier: "header")
self.collectionView.backgroundColor = UIColor(red: 0.0, green: 0.0, blue: 1.0, alpha: 0.5)
self.collectionView.dataSource = self
self.collectionView.delegate = self
self.collectionView.reloadData()
self.view.addSubview(self.collectionView)
self.collectionView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
self.collectionView.leftAnchor.constraint(equalTo: self.view.leftAnchor),
self.collectionView.rightAnchor.constraint(equalTo: self.view.rightAnchor),
self.collectionView.topAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.topAnchor),
self.collectionView.bottomAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.bottomAnchor)
])
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 2
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 5
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "default", for: indexPath)
cell.contentView.backgroundColor = UIColor.white
cell.contentView.layer.borderColor = UIColor.red.cgColor
cell.contentView.layer.borderWidth = 1.0
return cell
}
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
if indexPath.section == 0 && kind == UICollectionElementKindSectionHeader {
let header = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionElementKindSectionHeader, withReuseIdentifier: "header", for: indexPath)
return header
}
return collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "default", for: indexPath)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
if section == 0 {
return CGSize(width: collectionView.bounds.width, height: 200.0)
}
return .zero
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let layout = collectionViewLayout as! UICollectionViewFlowLayout
let insets = self.collectionView(collectionView, layout: layout, insetForSectionAt: indexPath.section)
var width = collectionView.bounds.width - 15.0
width -= insets.left + insets.right
return CGSize(width: floor(width), height: 100.0)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsetsMake(10.0, 10.0, 10.0, 10.0)
}
}
Related
I am trying to create a similar layout to the App store. I have got one collection view that is vertical and inside of it each row is another collection view that is horizontal.
I am trying to use dynamic height so the cell can increase its height based on the content inside. However that never works, it only works when i use sizeForItemAt function to explicitly set it. I would like to also be able to control the width
I have looked at many previous questions such as: UICollectionView, full width cells, allow autolayout dynamic height? however no answer worked for me.
I am really confused on what i am doing wrong.
import UIKit
class HomeViewController: UICollectionViewController {
public override func viewDidLoad() {
super.viewDidLoad()
let collectionViewLayout = UICollectionViewFlowLayout()
collectionViewLayout.scrollDirection = .vertical
collectionViewLayout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize
collectionView.collectionViewLayout = collectionViewLayout
collectionView.translatesAutoresizingMaskIntoConstraints = false
collectionView?.register(HorizontalCollectionView.self, forCellWithReuseIdentifier: HorizontalCollectionView.reuseIdentifier)
}
public override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 1
}
public override func numberOfSections(in collectionView: UICollectionView) -> Int {
return 4
}
public override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: HorizontalCollectionView.reuseIdentifier, for: indexPath) as? HorizontalCollectionView {
return cell
}
return UICollectionViewCell()
}
}
HorizontalCollectionView.swift
import UIKit
class HorizontalCollectionView: UICollectionViewCell, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
static let reuseIdentifier = "HorizontalCollectionView"
var cellSpancolumns: CGFloat?
override init(frame: CGRect) {
super.init(frame: frame)
collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "cell")
collectionView.delegate = self
collectionView.dataSource = self
addSubview(collectionView)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented - Storyboards are not used.")
}
lazy var collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
layout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize
let collectionView = UICollectionView(frame: bounds, collectionViewLayout: layout)
collectionView.translatesAutoresizingMaskIntoConstraints = false
collectionView.showsHorizontalScrollIndicator = false
return collectionView
}()
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 3
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath)
if (indexPath as NSIndexPath).section == 0 {
cell.backgroundColor = UIColor.blue
} else {
cell.backgroundColor = UIColor.red
}
return cell
}
}
I have a collection view with sticky headers
flowLayout.sectionHeadersPinToVisibleBounds = true
My issue is that the top half of my header is translucent and when I scroll my cell up I can see the cell scrolling behind the header.
I would like to hide the part of the cell view behind the header. In my image attached I do not want to see the green when it is behind the red. The rest of the behavior I want to keep as is.
The reason I need this is I have a wallpaper image at the very back which I need to be shown
#IBOutlet weak var collectionView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
self.collectionView.alwaysBounceVertical = true
collectionView.register(UINib.init(nibName: EXAMPLE_CELL_REUSE_ID, bundle: nil), forCellWithReuseIdentifier: EXAMPLE_CELL_REUSE_ID)
collectionView.register(UINib.init(nibName: EXAMPLE_HEADER_REUSE_ID, bundle: nil), forSupplementaryViewOfKind: UICollectionElementKindSectionHeader, withReuseIdentifier: EXAMPLE_HEADER_REUSE_ID)
if let flowLayout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout {
flowLayout.headerReferenceSize = CGSize(width: 400, height: 100)
flowLayout.sectionHeadersPinToVisibleBounds = true
}
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
return sections.count;
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 1 //self.sections[section].1;
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let exampleCell = collectionView.dequeueReusableCell(withReuseIdentifier: EXAMPLE_CELL_REUSE_ID, for: indexPath) as! MyCellCollectionViewCell;
exampleCell.headerLabel.text = "Cell"
exampleCell.backgroundColor = UIColor.green
return exampleCell;
}
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
if kind == UICollectionElementKindSectionHeader {
if let header = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionElementKindSectionHeader, withReuseIdentifier: EXAMPLE_HEADER_REUSE_ID, for: indexPath) as? ExampleHeader {
// header.backgroundColor = UIColor(red: 1.0, green: 0, blue: 0, alpha: 0.5)
return header
} else {
return UICollectionReusableView()
}
}
return UICollectionReusableView()
}
I think the question here may be similar but there are no responses and it is not quite clear if it is the same issue.
Transparent sticky header ui collectionview don't show cells underneath
This worked for me:
let layout = collectionView.collectionViewLayout as! UICollectionViewFlowLayout
layout.sectionHeadersPinToVisibleBounds = true
layout.sectionInsetReference = .fromSafeArea
view.backgroundColor = .white
collectionView.anchor(top: view.safeAreaLayoutGuide.topAnchor, leading:
view.leadingAnchor, bottom: view.bottomAnchor, trailing: view.trailingAnchor)
You need to apply mask for all cells that located under header view. Use on scrollViewDidScroll
Tutorial: https://medium.com/#peteliev/layer-masking-for-beginners-c18a0a10743
I have created a very simple setup like yours and it is working fine.
class ViewController: UIViewController, UICollectionViewDataSource
{
#IBOutlet weak var collectionView: UICollectionView!
override func viewDidLoad()
{
super.viewDidLoad()
self.collectionView.alwaysBounceVertical = true
if let flowLayout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout
{
flowLayout.headerReferenceSize = CGSize(width: 400, height: 100)
flowLayout.sectionHeadersPinToVisibleBounds = true
}
}
func numberOfSections(in collectionView: UICollectionView) -> Int
{
return 10
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
{
return 1
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{
return collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath)
}
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView
{
if kind == UICollectionElementKindSectionHeader
{
return collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionElementKindSectionHeader, withReuseIdentifier: "rview", for: indexPath)
}
return UICollectionReusableView()
}
}
I made a class GalleryCollectionViewController that inherited from UICollectionView like this:
import UIKit
class GalleryCollectionViewController: UICollectionViewController {
var dataSourceArr:Array<UIImage>!
override convenience init(collectionViewLayout layout: UICollectionViewLayout) {
self.init()
collectionView?.collectionViewLayout = layout
collectionView!.register(GalleryCollectionViewCell.self, forCellWithReuseIdentifier: "cell")
}
override func viewDidLoad() {
super.viewDidLoad()
}
// MARK: UICollectionViewDataSource
override func numberOfSections(in collectionView: UICollectionView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of items
if dataSourceArr.count != 0 {
return dataSourceArr.count
}
return 0
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! GalleryCollectionViewCell
cell.imageView.image = dataSourceArr[indexPath.row]
return cell
}
GalleryCollectionViewCell has defined.
And in root controller set this in viewDidLoad :
let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
layout.sectionInset = UIEdgeInsets(top: 20, left: 10, bottom: 10, right: 10)
layout.itemSize = CGSize(width: 90, height: 120)
let galleryColVC = GalleryCollectionViewController(collectionViewLayout: layout)
galleryColVC.dataSourceArr = photoLibraryImagesArr
self.present(galleryColVC, animated: true, completion: nil)
And but get this error in UICollectionView :
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'UICollectionView must be
initialized with a non-nil layout parameter'
Please help to fix this.
Here is small example
import UIKit
class ViewController: UIViewController {
let cellId = "cellId"
let newCollection: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
let collection = UICollectionView(frame: CGRect(x: 0, y: 0, width: 0, height: 0), collectionViewLayout: layout)
collection.translatesAutoresizingMaskIntoConstraints = false
collection.backgroundColor = UIColor.darkGray
collection.isScrollEnabled = true
// collection.contentSize = CGSize(width: 2000 , height: 400)
return collection
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(newCollection)
newCollection.delegate = self
newCollection.dataSource = self
newCollection.register(CustomeCell.self, forCellWithReuseIdentifier: cellId)
setupCollection()
}
func setupCollection(){
newCollection.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
newCollection.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
newCollection.heightAnchor.constraint(equalToConstant: 400).isActive = true
newCollection.widthAnchor.constraint(equalToConstant: view.frame.width).isActive = true
}
}
extension ViewController: UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout{
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 100
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = newCollection.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! CustomeCell
cell.backgroundColor = .white
cell.layer.cornerRadius = 5
cell.layer.borderWidth = 2
cell.layer.borderColor = UIColor.white.cgColor
cell.layer.shadowOpacity = 3
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: 150, height: 250)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsets(top: 3, left: 3, bottom: 3, right: 3)
}
}
class CustomeCell: UICollectionViewCell {
override init(frame: CGRect) {
super.init(frame: frame)
setupViews()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
I'm trying to build a collectionView that can expand multiple cells after they've been selected/tapped or collapsed when they are deselected, everything works fine when the cells remain on the screen, but once the expanded cells go off screen, I get unexpected behaviour.
For example if I select a cell with IndexPath 0 and then scroll down, tap on cell with IndexPath of 8, scroll back to cell with IndexPath 0 (it's already collapsed), I would tap on it and scroll back to the cell with IndexPath 8 and tap on it again it expands + cell with IndexPath 10 would expand too.
The CollectionView has been implemented programmatically as well the UICollectionViewCell has been subclassed.
ViewController that holds the UICollectionView:
import UIKit
class CollectionViewController: UIViewController {
// MARK: - Properties
fileprivate var collectionView: UICollectionView!
var manipulateIndex: NSIndexPath? {
didSet {
collectionView.reloadItems(at: collectionView.indexPathsForSelectedItems!)
}
}
// MARK: - Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
layout.sectionInset = UIEdgeInsets(top: 20, left: 10, bottom: 10, right: 10)
collectionView = UICollectionView(frame: self.view.frame, collectionViewLayout: layout)
collectionView.dataSource = self
collectionView.delegate = self
collectionView.register(CustomCell.self, forCellWithReuseIdentifier: "Cell")
collectionView.backgroundColor = UIColor.white
self.view.addSubview(collectionView)
}
}
// MARK: - UICollectionViewDataSource
extension CollectionViewController: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 13
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! CustomCell
cell.textLabel.text = "\(indexPath.item)"
cell.backgroundColor = UIColor.orange
return cell
}
}
// MARK: - UICollectionViewDelegate
extension CollectionViewController: UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, shouldSelectItemAt indexPath: IndexPath) -> Bool {
let cell = collectionView.cellForItem(at: indexPath) as! CustomCell
cell.expanded = !cell.expanded
manipulateIndex = indexPath as NSIndexPath
return false
}
}
// MARK: - UICollectionViewDelegateFlowLayout
extension CollectionViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
if let cell = collectionView.cellForItem(at: indexPath) as? CustomCell {
if cell.expanded == true {
return CGSize(width: self.view.bounds.width - 20, height: 300)
}
if cell.expanded == false {
return CGSize(width: self.view.bounds.width - 20, height: 120.0)
}
}
return CGSize(width: self.view.bounds.width - 20, height: 120.0)
}
}
And the subclassed custom UICollectionViewCell:
import UIKit
class CustomCell: UICollectionViewCell {
var expanded: Bool = false
var textLabel: UILabel!
override init(frame: CGRect) {
super.init(frame: frame)
textLabel = UILabel(frame: CGRect(x: 0, y: 0, width: frame.size.width, height: frame.size.height/3))
textLabel.font = UIFont.systemFont(ofSize: UIFont.smallSystemFontSize)
textLabel.textAlignment = .center
contentView.addSubview(textLabel)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Please help and thank you so much to the amazing person, who can help me out! :)
Try this:
Example 1: Expand only one cell at a time
Note: No need to take expanded bool variable in custom cell
var section:Int?
var preSection:Int?
var expand:Bool = false
extension ViewController: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 13
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! CustomCell
cell.textLabel.text = "\(indexPath.item)"
cell.backgroundColor = UIColor.orange
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if (self.section != nil) {
self.preSection = self.section
}
self.section = indexPath.row
if self.preSection == self.section {
self.preSection = nil
self.section = nil
}else if (self.preSection != nil) {
self.expand = false
}
self.expand = !self.expand
self.collectionView.reloadItems(at: collectionView.indexPathsForSelectedItems!)
}
}
// MARK: - UICollectionViewDelegateFlowLayout
extension ViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
if self.expand, let row = self.section, row == indexPath.row {
return CGSize(width: self.view.bounds.width - 20, height: 300)
}else{
return CGSize(width: self.view.bounds.width - 20, height: 120.0)
}
}
}
Example 2:
Expand multiple cell
import UIKit
class ViewController: UIViewController {
// MARK: - Properties
fileprivate var collectionView: UICollectionView!
var expandSection = [Bool]()
var items = [String]()
override func viewDidLoad() {
super.viewDidLoad()
self.items = ["A","B","C","D","E","F","G","H","J","K"]
let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
layout.sectionInset = UIEdgeInsets(top: 20, left: 10, bottom: 10, right: 10)
collectionView = UICollectionView(frame: self.view.frame, collectionViewLayout: layout)
collectionView.dataSource = self
collectionView.delegate = self
collectionView.register(CustomCell.self, forCellWithReuseIdentifier: "Cell")
collectionView.backgroundColor = UIColor.white
self.expandSection = [Bool](repeating: false, count: self.items.count)
self.view.addSubview(collectionView)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
extension ViewController: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.items.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! CustomCell
cell.textLabel.text = self.items[indexPath.row]
cell.backgroundColor = UIColor.orange
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
self.expandSection[indexPath.row] = !self.expandSection[indexPath.row]
self.collectionView.reloadItems(at: collectionView.indexPathsForSelectedItems!)
}
}
// MARK: - UICollectionViewDelegateFlowLayout
extension ViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
if self.expandSection[indexPath.row] {
return CGSize(width: self.view.bounds.width - 20, height: 300)
}else{
return CGSize(width: self.view.bounds.width - 20, height: 120.0)
}
}
}
UICollectionView will reuse your cells for multiple objects in your data model. You can't control which cells get reused when during reloadItems You should not assume that the expanded state in a given cell corresponds to the state of your data model. Instead, you should be holding onto the expanded state somehow in your data model and re-setting that in every call to cellForItemAt.
In other words, hold your state in your model and set the cell state in cellForItemAt, don't hold it in the cells themselves.
I am trying to add a headerCell to a UICollectionView and am trying to call the referenceSizeForHeaderInSection method. I have created a collectionViewCell (ProfileCell) and it builds correctly, until I call the the referenceSizeForHeaderInSection method, then it crashes...not sure why. Thanks in advance for any and all help!
// UICollectionViewCell class *******
class ProfileCell: UICollectionViewCell {
override init(frame: CGRect) {
super.init(frame: frame)
setupViews()
}
let wordLabel: UILabel = {
let label = UILabel()
label.text = "test test test"
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
func setupViews() {
backgroundColor = .red
addSubview(wordLabel)
wordLabel.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
wordLabel.leftAnchor.constraint(equalTo: self.leftAnchor).isActive = true
wordLabel.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
wordLabel.rightAnchor.constraint(equalTo: self.rightAnchor).isActive = true
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
// UIViewController *******
class HomeController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout, FBSDKLoginButtonDelegate {
lazy var collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
layout.minimumLineSpacing = 0
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
cv.delegate = self
cv.dataSource = self
cv.isPagingEnabled = true
return cv
}()
let cellId = "cellId"
let headerId = "headerId"
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(collectionView)
// use autolayout instead
collectionView.anchorToTop(view.topAnchor, left: view.leftAnchor, bottom: view.bottomAnchor, right: view.rightAnchor)
collectionView.backgroundColor = .white
collectionView.register(ProfileCell.self, forCellWithReuseIdentifier: cellId)
collectionView.register(UICollectionViewCell.self, forSupplementaryViewOfKind: UICollectionElementKindSectionHeader, withReuseIdentifier: headerId)
fileprivate func registerCells() {
collectionView.register(ProfileCell.self, forCellWithReuseIdentifier: cellId)
}
// set up collectionView
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 4
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath)
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: view.frame.width, height: 150)
}
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
let header = collectionView.dequeueReusableCell(withReuseIdentifier: headerId, for: indexPath)
return header
}
// crashes when I call this method
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
return CGSize(width: view.frame.width, height: 50)
}
}