It's been a while since I programmatically created a collection view and I can't seem to find why my cells are not showing. Delegate and Datasource are being set. Size is being set and it is not too big.
class ViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
let cellId = "cellId"
override func viewDidLoad() {
super.viewDidLoad()
setupCV()
}
var containerCollectionView: UICollectionView = {
let layout = UICollectionViewLayout()
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
cv.backgroundColor = UIColor.white
return cv
}()
}
extension ViewController {
fileprivate func setupCV() {
// these first two lines add the colelction view and apply constraints - they work and the CV is properly displayed inside the view controller
self.view.addSubview(containerCollectionView)
containerCollectionView.anchor(view.topAnchor, left: view.leftAnchor, bottom: view.bottomAnchor, right: view.rightAnchor, topConstant: 0, leftConstant: 0, bottomConstant: 0, rightConstant: 0, widthConstant: 0, heightConstant: 0)
containerCollectionView.delegate = self
containerCollectionView.dataSource = self
containerCollectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: cellId)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: 150, height: 150)
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath)
cell.backgroundColor = UIColor.yellow
return cell
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 3
}
}
You are initializing the wrong layout.
try this:
let layout = UICollectionViewFlowLayout()
Related
Pure code (I didn't use the storyboard, just use code) builds the collection but UICollectionViewDelegateFlowLayout is not executed.
class Calendarview : UIView , UICollectionViewDelegate , UICollectionViewDataSource , UICollectionViewDelegateFlowLayout {
var collectionview : UICollectionView!
override func didAddSubview(_ subview: UIView) {
}
override func didMoveToSuperview() {
let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
collectionview = UICollectionView(frame: CGRect(x: 0, y: 0, width: self.bounds.width, height: self.bounds.height) , collectionViewLayout: layout)
collectionview.frame = self.frame
collectionview.register(CalendarCell.self, forCellWithReuseIdentifier: "Cell")
collectionview.dataSource = self
collectionview.delegate = self
self.addSubview(collectionview!)
collectionview.reloadData()
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 42
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForFooterInSection section: Int) -> CGSize {
return CGSize(width: self.bounds.width / 7, height: self.bounds.height / 3)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 5
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 5
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! CalendarCell
cell.lbshow = UILabel(frame: cell.bounds)
cell.lbshow.text = String(indexPath.row)
cell.addSubview(cell.lbshow)
cell.backgroundColor = .blue
return cell
}
}
Set delegate as self.collectionView.delegate = self;. UICollectionViewDelegateFlowLayout inherits from UICollectionViewDelegate. So it will called all methods of UICollectionViewDelegateFlowLayout.
Hence, You need to set self.collectionView.collectionViewLayout to your flow layout.
let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
layout.estimatedItemSize = CGSize(width: 10, height: 10)
layout.itemSize = CGSize(width: 200, height: 200)
layout.minimumLineSpacing = 10
layout.sectionInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
layout.scrollDirection = .horizontal
collectionview.collectionViewLayout = layout
I have a UICollectionView that I have implemented to scroll horizontally but for some reason when I scroll through there are only 3 cells that are presented (when there should be 9)
class BusinessPhotosViewController: UICollectionViewController ,UICollectionViewDelegateFlowLayout{
let cellId = "photos"
override func viewDidLoad() {
super.viewDidLoad()
if let layout = collectionView?.collectionViewLayout as? UICollectionViewFlowLayout {
layout.scrollDirection = .horizontal
layout.minimumLineSpacing = 0
}
collectionView?.register(BusinessPhotoCells.self, forCellWithReuseIdentifier: cellId)
collectionView?.contentInset = UIEdgeInsets.init(top: 0, left: 0, bottom: 0, right: 0)
collectionView?.scrollIndicatorInsets = UIEdgeInsets.init(top: 0, left: 0, bottom: 0, right: 0)
collectionView?.isPagingEnabled = true
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 9
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 0
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! BusinessPhotoCells
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let width = view.frame.width / 3
return CGSize(width: width, height: width)
}
}
I present this BusinessPhotosViewController within the cell of ANOTHER UICollectionViewController called BusinessDetailViewController
class BusinessDetailViewController : UICollectionViewController {
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let scrollCell = collectionView.dequeueReusableCell(withReuseIdentifier: pictureCellId, for: indexPath)
let bizVC = BusinessPhotosViewController(collectionViewLayout: UICollectionViewFlowLayout())
scrollCell.addSubview(bizVC.view)
bizVC.view.anchor(top: scrollCell.topAnchor, left: scrollCell.leftAnchor, bottom: scrollCell.bottomAnchor, right: scrollCell.rightAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: scrollCell.frame.width, height: scrollCell.frame.height)
return scrollCell
}
}
So I can see that the scrolling works, but the issue I'm seeing is that only 3 cells are loaded (out of 9) and the view looks something like this:
I've seen these solutions here and here and other but none helped me.
I tried your code and I can see all the 9 cells if the size of 9 cell is smaller than the size scroll cell size.
import UIKit
class ViewController: UIViewController{
override func viewDidLoad() {
view.backgroundColor = .red;
view.addSubview(photosContainer);
photosContainer.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true;
photosContainer.topAnchor.constraint(equalTo: view.centerYAnchor).isActive = true;
photosContainer.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true;
photosContainer.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.4).isActive = true;
***let layout = UICollectionViewFlowLayout();
layout.scrollDirection = .horizontal
let bizVC = BusinessPhotosViewController(collectionViewLayout: layout)
bizVC.view.frame = photosContainer.bounds;
photosContainer.addSubview(bizVC.view)***
}
let photosContainer : UIView = {
let view = UIView();
view.backgroundColor = .green;
view.translatesAutoresizingMaskIntoConstraints = false;
return view;
}();
}
Before adding the view if you set the frame then I can see all the nine cells no matter their size.
Let me know if it works
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 want to show banner like this:
My approach is adding a CollectionView as a TableViewHeader
My code:
extension HomeViewController: UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
func configureHeaderView() {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
layout.sectionInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
let headerView = UICollectionView(frame: CGRect(x: 0, y: 0, width: view.frame.width, height: headerHeight), collectionViewLayout: layout)
headerView.backgroundColor = .blue
headerView.isPagingEnabled = true
headerView.isUserInteractionEnabled = true
headerView.dataSource = self
headerView.delegate = self
headerView.register(BannerCollectionViewCell.self, forCellWithReuseIdentifier: BannerCollectionViewCell.reuseIdentifier)
headerView.showsHorizontalScrollIndicator = false
tableView.tableHeaderView = headerView
}
// MARK: UICollectionViewDataSource
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 3
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: BannerCollectionViewCell.reuseIdentifier, for: indexPath) as! BannerCollectionViewCell
return cell
}
// MARK: UICollectionViewDelegateFlowLayout
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: UIScreen.main.bounds.width, height: headerHeight)
}
}
My BannerCollectionViewCell has a default image.
class BannerCollectionViewCell: UICollectionViewCell {
#IBOutlet weak var bannerImageView: UIImageView!
}
But I don't see that image on my header. It just show an empty header.
you use the NIB, so you should use func register(UINib?, forCellWithReuseIdentifier: String) instead of func register(AnyClass?, forCellWithReuseIdentifier: String)
Maybe you're looking for an image pager like KIImagePager on the top instead of a collection view. You can also create the same using inbuilt PageViewController.
You can add a TableView separately below this.
I'm new to iOS development.
I've been learning Swift, and today, I tried using UICollectionViewController.
My code is as follows:
class ViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout {
var colView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
layout.sectionInset = UIEdgeInsets(top: 20, left: 10, bottom: 10, right: 10)
layout.itemSize = CGSize(width: 90, height: 120)
colView = UICollectionView(frame: self.view.frame, collectionViewLayout: layout)
colView.dataSource = self
colView.delegate = self
colView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "Cell")
colView.backgroundColor = UIColor.white
self.view.addSubview(colView)
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 14
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath as IndexPath)
cell.backgroundColor = UIColor.orange
return cell
}
}
However, when I try to run it, I get the following error:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'UICollectionView must be initialized with a non-nil layout parameter'
I have looked up previous posts discussing this issue, but I couldn't find anything that fixed my code.
How should I fix this?
Thank you! I incorporated your suggestions. Below is the code that works:
class ViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
let cellId = "CellId"
var colView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
let layout = UICollectionViewFlowLayout()
layout.sectionInset = UIEdgeInsets(top: 20, left: 10, bottom: 10, right: 10)
layout.itemSize = CGSize(width: 111, height: 111)
colView = UICollectionView(frame: self.view.frame, collectionViewLayout: layout)
colView.delegate = self
colView.dataSource = self
colView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "Cell")
colView.backgroundColor = UIColor.white
self.view.addSubview(colView)
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 21
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath as IndexPath)
cell.backgroundColor = UIColor.orange
return cell
}
}
Again, thank you guys very much. Have an awesome day :D
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
let cellSize = CGSize(width: 90, height: 120)
//
return CGSizeMake(cellSize)
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAtIndex section: Int) -> UIEdgeInsets {
return UIEdgeInsetsMake(top: 20, left: 10, bottom: 10, right: 10)
}
This should work:
lazy var collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
return collectionView
}()
Just change the order like below one :
let layout = UICollectionViewFlowLayout()
layout.sectionInset = UIEdgeInsets(top: 20, left: 10, bottom: 10, right: 10)
layout.itemSize = CGSize(width: 90, height: 120)
colView = UICollectionView(frame: self.view.frame, collectionViewLayout: layout)
self.view.addSubview(colView)
colView.delegate = self
colView.dataSource = self
colView.registerClass(UICollectionViewCell.self, forCellWithReuseIdentifier: "Cell")
colView.backgroundColor = UIColor.white
You can set your code like this:
class ViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
let cellId = "Cell"
lazy var collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
// ... layout parameters
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
cv.backgroundColor = .white
cv.delegate = self
cv.dataSource = self
return cv
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(collectionView)
// ... add your auto layout constraints to the collection view
collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: cellId)
}
// Delegate and Data Source Methods...
}