Multiple CollectionViews in one ViewController - ios

I try to show multiple collection views in one ViewController, but I get crash with error:
Thread 1: Exception: "could not dequeue a view of kind: UICollectionElementKindCell with identifier MainCell - must register a nib or a class for the identifier or connect a prototype cell in a storyboard"
I registered all the cells in Storyboard and this is my code for ViewController:
class ViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
#IBOutlet weak var collectionView: UICollectionView!
#IBOutlet weak var horizontalNumbers: UICollectionView!
#IBOutlet weak var verticalNumbers: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
collectionView.delegate = self
collectionView.dataSource = self
horizontalNumbers.delegate = self
horizontalNumbers.dataSource = self
verticalNumbers.delegate = self
verticalNumbers.dataSource = self
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if collectionView == collectionView {
return 81
} else {
return 9
}
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if collectionView == collectionView {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "MainCell", for: indexPath) as! CollectionViewCell
cell.backgroundColor = .systemGreen
return cell
} else if collectionView == horizontalNumbers {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Horizontal", for: indexPath) as! HorizontalNumbersCell
return cell
} else {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Vertical", for: indexPath) as! VerticalNumbersCell
return cell
}
}
}
I checked everything twice and looked for some examples of code, but don't release why I get crash.

You need to register cell for all three collectionView.Take the collectionView for example.
try
collectionView.register(UINib(nibName: "CollectionViewCell", bundle: nil), forCellWithReuseIdentifier: "MainCell") // if it is loaded in nib, and nib name is CollectionViewCell
or
collectionView.register(CollectionViewCell.self, forCellWithReuseIdentifier: "MainCell") // if it is written just in code
in the viewDidLoad.
Do the same thing for the horizontalNumbers, etc.
Check https://developer.apple.com/documentation/uikit/uicollectionview/1618089-register for more details.

Related

IBOutelt found nil when UICollectionViewCell is load

I am studying how to create a simple app without storyboard and I have a problem:
I created a XIB called HomeViewController, inside it has a UICollectioView and made the registration of the cell in the viewDidLoad().
after that, I created a cell called HomeCollectionViewCell, put the identifier as "cell", in the cell it has a UILabelOutlet, that is referenced in the .swift file.
After setting the delegate and the datasource, it has the mandatory methods of the datasource, when making the cell configuration and calling the label by setting any text it returns nil. I don't know why this is happening, I've seen several videos on YouTube and none has solved the problem. Can anybody help me?
HomeViewController
class HomeViewController: UIViewController {
#IBOutlet weak var collectionView: UICollectionView!
var info = ["Image"]
override func viewDidLoad() {
super.viewDidLoad()
collectionView.register(HomeCollectionViewCell.self, forCellWithReuseIdentifier:
"cell")
collectionView.delegate = self
collectionView.dataSource = self
}
}
extension HomeViewController: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection
section: Int) -> Int {
return info.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath:
IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for:
indexPath) as? HomeCollectionViewCell
cell?.teste.text = "test"
return cell!
}
}
HomeCollectionViewCell
class HomeCollectionViewCell: UICollectionViewCell {
#IBOutlet weak var teste: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
}
}
That's because you have to register cell's nib not just class:
collectionView.register(UINib(nibName: "HomeCollectionViewCell", bundle: nil), forCellWithReuseIdentifier: "cell")

How to push a new UICollectionViewController to the navigationController inside a cell of cells of cells

I have build a UICollectionViewController programmatically which returns 4 cells. HomeCell, TrendingCell, SubscriptionCell and AccountCell.
All 4 cells should be different and you can scroll them horizontally. <--->.
class HomeController: UICollectionViewController, UICollectionViewDelegateFlowLayout{
override func viewDidLoad() {
super.viewDidLoad()
collectionView?.register(HomeCell.self, forCellWithReuseIdentifier: homeCellId)
collectionView?.register(TrendingCell.self, forCellWithReuseIdentifier: trendingCellId)
collectionView?.register(SubscriptionCell.self, forCellWithReuseIdentifier: subscriptionCellId)
collectionView?.register(AccountCell.self, forCellWithReuseIdentifier: accountCellId)
}
}
Lets take the first cell of the HomeController called HomeCell to illustrate my problem. Homecell has three custom cells called VideoCell, CategoryCell and UserSearchCell.
class HomeCell: UICollectionViewCell, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
let cellId = "cellId"
let searchId = "sarchId"
let scrollId = "scrollId"
lazy var collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
cv.backgroundColor = UIColor.white
cv.dataSource = self
cv.delegate = self
return cv
}()
override func setupViews() {
super.setupViews()
.....
// register three different cells within HomeCell
collectionView.register(VideoCell.self, forCellWithReuseIdentifier: cellId)
collectionView.register(CategoryCell.self, forCellWithReuseIdentifier: scrollId)
collectionView.register(UserSearchCell.self, forCellWithReuseIdentifier: searchId) //
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
In HomeCell I register the UserSearchCell as third cell.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if indexPath.item == 0 {
cell = collectionView.dequeueReusableCell(withReuseIdentifier: "videoId", for: indexPath)
} else if indexPath.item == 1{
cell = collectionView.dequeueReusableCell(withReuseIdentifier: categoryId, for: indexPath)
}else {
cell = collectionView.dequeueReusableCell(withReuseIdentifier: searchId, for: indexPath)
}
return cell
}
If I click on that element my goal is to push a new ViewController to the navigationController. But I have no access and don't know how to change the view within that nested structure. I tried the didSelectItem method within HomeCell class and was able to print something the console when clicking the third cell but couldn't change the view.
class HomeCell: UICollectionViewCell, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if indexPath.item == 2 {
print(123)
}
....
}
Please help. Is there a way to change the View within the didSelectItem method of HomeCell ??
You need to write a protocol to refer back to your HomeController.
protocol HomeCellProtocol {
func pushNavigation(_ vc: UIViewController)
}
add write delegate property to HomeCell class with
class HomeCell: ..... {
var delegate: HomeCellProtocol?
}
and make HomeController confirm to HomeCellProtocol with
extention HomeController: HomeCellProtocol {
func pushNavigation(_ vc: UIViewController) {
self.navigationController?.pushViewController(vc, animated: true)
}
}
and when you setup your HomeCell you need to set your delegate in HomeController
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
cell = collectionView.dequeueReusableCell(withReuseIdentifier: homeCellId, for: indexPath) as HomeCell;
cell.delegate = self // Set the delegate
return cell
}
finally, you can call push function within HomeCell with
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if indexPath.item == 2 {
let vc = UIViewController();
self.delegate?.pushNavigation(vc);
}
}

Two CollectionViews on one View Controller

I am trying to have two collection views on one view controller. There are many posts guiding people through this but I cannot tell what I am doing wrong.
My code is the following,
class FeedVC: UIViewController {
#IBOutlet var collectionView: UICollectionView!
#IBOutlet var collectionViewProgress: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
collectionView.dataSource = self
collectionView.delegate = self
collectionViewProgress.dataSource = self
collectionViewProgress.delegate = self
}
}
extension FeedVC: UICollectionViewDelegate, UICollectionViewDataSource{
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if (collectionView == self.collectionView){
return 10
}else{
return 1
}
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
print("hre")
if (collectionView == self.collectionView){
if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "UnclaimedCVC", for: indexPath) as? UnclaimedCVC {
cell.configureCell()
return cell
}else{
return UICollectionViewCell()
}
}else if(collectionView == self.collectionViewProgress){
if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "InProgressCVC", for: indexPath) as? InProgressCVC {
cell.configureCell()
return cell
}else{
return UICollectionViewCell()
}
}else{
return UICollectionViewCell()
}
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
if (collectionView == self.collectionView){
return 1
}else{
return 1
}
}
}
I am currently getting the error,
'could not dequeue a view of kind: UICollectionElementKindCell with
identifier InProgressCVC - must register a nib or a class for the
identifier or connect a prototype cell in a storyboard'
In viewDidLoad put
collectionView.register(UnclaimedCVC.self, forCellWithReuseIdentifier: "UnclaimedCVC ")
collectionViewProgress.register(InProgressCVC.self, forCellWithReuseIdentifier: "InProgressCVC ")
If you created them as Xibs
collectionView.register(UINib.init(nibName: "UnclaimedCVC", bundle: nil), forCellWithReuseIdentifier: "UnclaimedCVC")
collectionViewProgress.register(UINib.init(nibName: "InProgressCVC", bundle: nil), forCellWithReuseIdentifier: "InProgressCVC")

Unable to load collectionview inside UItableviewcell

Collectionview reload not working when reload from custom tablecell.
I have put a dropdown inside one collectionview cell.this dropdown also not working.
Here is the code:
class HotelAvailableRoomsTVCell: UITableViewCell {
#IBOutlet weak var collectionView: UICollectionView!
//MARK: Life cycle
override func awakeFromNib() {
super.awakeFromNib()
let nib = UINib.init(nibName: "AvailableRoomCVCell", bundle: nil)
collectionView.register(nib, forCellWithReuseIdentifier: "AvailableRoomCVCell")
collectionView.delegate = self
collectionView.dataSource = self
// Initialization code
}
func initCellWithData(roomJSON:JSON,indexPath:IndexPath,isSingle:Bool) {
DispatchQueue.main.async {
self.collectionView.reloadData()
}
}
}
In viewController:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "HotelAvailableRoomsTVCell", for: indexPath) as! HotelAvailableRoomsTVCell
cell.delegate = self
cell.initCellWithData(roomJSON: (json)!, indexPath: indexPath as IndexPath, isSingle: true)
return cell;
}
Screen is:
I have used a tableview with dynamic number of cells as collectionview. this collection view contains a dropdown as shown in image
First of all, do this & check
class HotelAvailableRoomsTVCell: UITableViewCell, UICollectionViewDelegate,UICollectionViewDataSource
You can also do this, (already done by me and its working),
Pass the data by creating var in HotelAvailableRoomsTVCell.
var collectionData : [String : Any] = []
In cellForRow
cell.collectionData = data

Subclassing UICollectionViewCell and initialising from xib

I am registering the cell like this:
collectionView!.registerNib(UINib(nibName: "ItemCell", bundle: nil), forCellWithReuseIdentifier: "ItemCellIdentifier")
and accessing it like this:
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("ItemCellIdentifier", forIndexPath: indexPath) as! ItemCell
ItemCell is the subclass of UICollectionViewCell:
class ItemCell: UICollectionViewCell {
#IBOutlet weak var itemImage: UIImageView!
#IBOutlet weak var itemImageDescription: UILabel!
//creation from nib requires this method to be overridden
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
Finally I have my xib with a UICollectionViewCell with class ItemCell.
I keep getting the error:
Assertion failure in -[UICollectionView
_dequeueReusableViewOfKind:withIdentifier:forIndexPath:viewCategory:], /SourceCache/UIKit/UIKit-3347.44.2/UICollectionView.m:3443.
The crash is on this line:
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("ItemCellIdentifier", forIndexPath: indexPath) as! ItemCell
EDIT
collectionView!.registerNib(UINib(nibName: "ItemCell", bundle: nil), forCellWithReuseIdentifier: "ItemCellIdentifier")
is called from
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
}
contained in:
class CollectionCell : UITableViewCell, UICollectionViewDataSource, UICollectionViewDelegate {
Where are you calling registerNib?
I reproduced the setup to match what i can tell from yours.
This works for me, no errors :
import UIKit
class ViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate {
var model:[String] = []
#IBOutlet weak var collectionView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
// just a simple test model ...
for index in 1...100 {
model.append("\(index)")
}
// register the nib
collectionView.registerNib(UINib(nibName: "ItemCell", bundle: nil), forCellWithReuseIdentifier: "ItemCell")
// this is just a test layout
var layout = UICollectionViewFlowLayout()
layout.itemSize = CGSize(width:200.0,height:200.0)
layout.scrollDirection = UICollectionViewScrollDirection.Vertical
self.collectionView.collectionViewLayout = layout
self.collectionView.delegate = self;
self.collectionView.dataSource = self;
self.collectionView.reloadData()
}
// MARK: - UICollectionViewDataSource
func collectionView(collectionView: UICollectionView,
numberOfItemsInSection section: Int) -> Int{
return model.count
}
func collectionView(collectionView: UICollectionView,
cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell{
var cell:UICollectionViewCell = collectionView.dequeueReusableCellWithReuseIdentifier("ItemCell", forIndexPath: indexPath) as! UICollectionViewCell
return cell
}
}
Try to search in main bundle
collectionView!.registerNib(UINib(nibName: "ItemCell", bundle: NSBundle.mainBundle()), forCellWithReuseIdentifier: "ItemCellIdentifier")
The crash is because the downcasting has failed. Instead of using forced form as! use the conditional one as?
Try this other way to register cells:
ViewDidLoad
collectionView.registerClass(ItemCell.self,forCellWithReuseIdentifier:"ItemCellIdentifier")
cellForItemAtIndexPath
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath:NSIndexPath)->UICollectionViewCell
{
var cell = collectionView.dequeueReusableCellWithReuseIdentifier("ItemCellIdentifier", forIndexPath: indexPath) as ItemCell
cell.titleLabel.text="cellText"
return cell
}

Resources