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")
Related
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.
I keep getting this error message. I have already tried to add this in my code:
self.collectionView.register(UICollectionCell.self, forCellReuseIdentifier: "Cell")
But that just brings up the another error:
Ambiguous reference to member 'collectionView(_:numberOfItemsInSection:)'
Here is my code:
class HomeViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate {
#IBOutlet weak var UICollectionView: UICollectionView!
let items = ["0", "1", "2", "3", "4"]
override func viewDidLoad() {
self.UICollectionView.register(CollectionViewCell.self, forCellWithReuseIdentifier: "Cell")
super.viewDidLoad()
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return items.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! CollectionViewCell
cell.mainListLabel.text = items[indexPath.item]
return cell
}
}
Change
self.collectionView.register(UICollectionCell.self, forCellReuseIdentifier: "Cell")
to
self.collectionView.register(UICollectionViewCell.self, forCellReuseIdentifier: "Cell")
And
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! UICollectionViewCell
What's the CollectionViewCell stands for? It's derived class or something?
Because you register UICollectionCell but call CollectionViewCell ?
I hope it works.
// edit
Final Solution:
class CollectionViewCell: UICollectionViewCell {
#IBOutlet weak var mainListButton: UIButton!
#IBOutlet weak var mainListLabel: UILabel!
override init(frame: CGRect) {
super.init(frame: frame)
self.backgroundColor = UIColor.black
}
required init?(coder: NSCoder) {
super.init(coder: coder)
self.backgroundColor = UIColor.black
}
}
Set your custom collection view class or use XIB.
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);
}
}
this is my class for ImageController (a ViewController)
Only the background of the collection view is displaying while the cells in it are not. Any help? Is there something I didn't initialize correctly?
Here is the class:
class ImageController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate {
let reuseIdentifier = "imageCell"
#IBOutlet weak var collectionView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 10
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath as IndexPath) as! ImageCell
cell.myImage.image = unknown //name of image, isn't the cause of error
return cell
}
}
You should add in the viewDidLoad():
collectionView.delegate = self
collectionView.dataSource = self
Also, try to debug the methods (add breakpoints and check if they are reachable).
Hope this helped.
If you dont want to use the lines of code mentioned above, just right click on the collectionView and drag to ViewController Scene the yellow space and select delegate and dataSoruce.
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
}