I have a view HeaderView that is subclassing UICollectionReusableView and which is a UIStackView arranging some subviews with labels. The number and texts of those labels depend of the information requested to a service.
Such view is registered to a collectionView this way:
collectionView.register(HeaderView.self, forSupplementaryViewOfKind: UICollectionElementKindSectionHeader, withReuseIdentifier: HeaderView.reuseIdentifier)
Then I've implemented collectionView(_:viewForSupplementaryElementOfKind:at:) method like this:
public func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
switch kind {
case UICollectionElementKindSectionHeader:
if let headerView = collectionView.dequeueReusableSupplementaryView(ofKind: kind,
withReuseIdentifier: HeaderView.reuseIdentifier,
for: indexPath) as? HeaderView {
// Provide the headerView with the texts it needs to show
return headerView
}
fatalError("no view")
default:
fatalError("no view")
}
}
and also implemented collectionView(_:layout:referenceSizeForHeaderInSection:) method.
My issue is that collectionView(_:layout:referenceSizeForHeaderInSection:) is called first, and at that moment the headerView has not been instantiated, so I can't get its actual height. I can't provide a fix height either because, as I said, the height of such headerView will depend on the information I get from a service and it's going to be variable.
I'm not using either storyboard or xib files, I'm creating all the views and setting the layout in code.
I should also add that actually I need this HeaderView to be the header of just one section in my collection view, so I may not need to dequeue and reuse it?
I'm new to iOS programming but from my experience with Android i know that you can create one design for a item that can be reused in multiple lists, without needing to create a copy.
In iOS i have been trying the same approach with UICollectionView.
In my view i have created three Horizontal UICollectionViews (with different data sets) which in turn use their own cell (Which is identical to the others). I don't know how to make the other two collections use the first ones cell, so i don't need to recreate the same cell over and over.
You can't achieve this using single storyboard. If you want to use Interface Builder (xml) file for layout, you should create MyCell.xib file, then drag UICollectionViewCell on that file and work with that xib.
Then you should connect .xib file with your collection view like this:
let nib = UINib(nibName: "MyCell", bundle: nil)
collectionView?.register(nil, for: "MyCellReuseID")
And you can work with your cell now.
Also you can create MyCell: UICollectionViewCell class without creating .xib file, create views on it programmatically and then call collectionView?.register(MyCell.self, for: "MyCellReuseID")
In your function collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell you can inform your collectionview which cell to use
if you have define your cell you might have something similar to this
class MyCell: UICollectionViewCell{
var Label: UILabel!
var imageView: UIImageView!
}
so into your function collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) you need to inform your collectionview which cell to use
collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell:MyCell = collectionView.dequeueReusableCellWithReuseIdentifier("myCell", forIndexPath: indexPath) as! MyCell
[...]
}
and dont forget to register your cell first
override func viewDidLoad() {
super.viewDidLoad()
//do your stuff
collectionView.delegate = self
collectionView.dataSource = self
collectionView.backgroundColor = UIColor.whiteColor()
collectionView.registerClass(MyCell.self, forCellWithReuseIdentifier: "myCell")
}
and Voila !
Create new file collection cell with XIB
In your "item at row for index" delegate method in view controller, initialize cell and return it.
Now any collection view within you view controller have the same cell
Create a Swift class(CollectionViewCell) by having UICollectionViewCell as a base class. Select create nib option while doing that.
Then create a view controller and add collection view to it in your storyboard and hook up this custom cell class in Identity Inspector. Basically, your structure needs to look like below
I have 2 views on top of my CollectionView and just want to scroll on all these 3 views. I understand the best approach is to place these 2 top views in my collectionView header. How can I achieve this in storyboard (interface builder) without any code?
(I use this way in my another tableView but I have can't do this with CollectionoView)
I drag those 2 views to my collectionView's reusableView(headerView) and I've faced with this error:
enter image description here
There is no way you can drag and drop in interface builder and get header view for collection view. You have to additionally implement viewForSupplementaryElementOfKind method which returns UICollectionReusableView. While using collection reusable view, we have to treat that view in different way similar to stuffs we do for reusable cell.
Steps to follow.
Create a class (HeaderViewExample) for that header view.
Assign class (HeaderViewExample) to the reusable view you have just added in interface builder.
Give a reusable identifier (HeaderViewExample) to that reusable view.
Now you add label or buttons to the reusable view and create outlets for those outlets in the Class HeaderViewExample.
(Note: While using reusable views, don't create outlets directly in the controller.)
Now Update your CollectionViewController with the below code.
override func collectionView(collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, atIndexPath indexPath:
NSIndexPath) -> UICollectionReusableView {
var reusableView = UICollectionReusableView()
if kind == UICollectionElementKindSectionHeader {
guard let view = collectionView?.dequeueReusableSupplementaryViewOfKind(kind, withReuseIdentifier: String(HeaderViewExample), forIndexPath: indexPath) as? HeaderViewExample else { return reusableView }
view.label = "Test String"
view.backgroundColor = UIColor.redColor()
} else {
assert(false, "Unexpected element kind")
}
return reusableView
}
I have a static table with one static section. Other sections are dynamic.
I create Table Section and Table Cell for dynamic section. Set identifier for Cell, set custom class for it and even do:
self.tableView.registerClass(UncheckedStoreTableViewCell.self, forCellReuseIdentifier: "StoreCell")
if i don't register it with code, then i get:
'unable to dequeue a cell with identifier StoreCell - must register a
nib or a class for the identifier or connect a prototype cell in a
storyboard'
So when i use this:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if indexPath.section == 0 {
return super.tableView(tableView, cellForRowAtIndexPath: indexPath)
}
let cell = tableView.dequeueReusableCellWithIdentifier("StoreCell", forIndexPath: indexPath) as! UncheckedStoreTableViewCell
return cell
}
It works. But if i'm trying to change label: cell.myLabel.text = "one"
or just print(cell.myLabel) got
BAD_INSTRUCTION
You can definitely use dynamic cells in a static table view.
Don't expect a static table view to register your cell's identifier for you. Just do it yourself.
Do you have outlets in the cell class to some view in interface builder? If I were you I wouldn't expect the table view to know about that. It will instantiate your cell class, and that's it. No outlets will be set. I think this is related: load nib in view subclass
By the way, if you've defined a custom .nib for your cell, there's this method: registerNib(_:forCellReuseIdentifier:)
You do not need to register your cell in code.
You have correctly set the identifier of the cell, however this is not enough. In addition to this you also need to open Identity Inspector for your cell and set the class of the cell to be UncheckedStoreTableViewCell. Here is an image showing you where you should set it:
Without this step Xcode will not be able to correctly associate your cell identifier with your custom cell as it doesn't know anything about it!
Taking first plunge with collection views and am running into this error:
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'could not dequeue a view of kind: UICollectionElementKindCell with identifier Cell - must register a nib or a class for the identifier or connect a prototype cell in a storyboard'
The code is very simple, as shown below. I can't for the life of me figure out what it is that I'm missing.
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"Cell" forIndexPath:indexPath];
cell.backgroundColor = [UIColor whiteColor];
return cell;
}
The collection view controller was created using a nib and the delegates & datasources are both set to file's owner.
View Controller's header file is also really basic.
#interface NewMobialViewController_3 : UICollectionViewController <UICollectionViewDataSource, UICollectionViewDelegateFlowLayout>
#end
From the UICollectionView documentation for the dequeue method:
Important: You must register a class or nib file using the registerClass:forCellWithReuseIdentifier: or registerNib:forCellWithReuseIdentifier: method before calling this method.
You need to use same identifier between the dequeueReusableCellWithReuseIdentifier's argument and the UICollectionViewCell's property.
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"CollectionViewCell" forIndexPath:indexPath];
Complementing what #jrtuton written... What you need is:
1) Register your nib file to "connect" it with your identifier:
//MyCollectionView.m
- (void) awakeFromNib{
[self registerNib:[UINib nibWithNibName:#"NibFileName" bundle:nil] forCellWithReuseIdentifier: #"MyCellIdentifier"];
}
2) Use your identifier to load your custom cell from a nib:
//MyCollectionView.m
- (UICollectionViewCell *)collectionView:(UICollectionView *)cv cellForItemAtIndexPath:(NSIndexPath *)indexPath {
MyCustomCollectionViewCell* cell = [cv dequeueReusableCellWithReuseIdentifier:#"MyCellIdentifier" forIndexPath:indexPath];
}
3) Use always static NSString* to avoid the same identifiers again in your app.
Swift 5
1) Make sure you have a correct deque for HEADER, you might be using the regular for normal cells.
override func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: headerId, for: indexPath)
return header
}
2) doubleCheck the registration (ViewDidLoad)
collectionView.register(HeaderCell.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: headerId)
i had everything 100% done correctly.
but for some reason i got the same error for an hour, then what i did is:
go to storyboard
select the prototype cell (in my case)
clear the class name from identity inspector, then rewrite it
rewrite the identifier name in the attributes inspector
simply, redid this step even tho i made it correct before, its like xcode misses something at a specific nanosecond!
Swift4.0
Whenever your UITableViewCell is xib at that time you must have to register with UITableView.
override func viewDidLoad(){
super.viewDidLoad()
self.yourtableview.register(UINib(nibName: "yourCellXIBname", bundle: Bundle.main), forCellReuseIdentifier: "YourCellReUseIdentifier")
}
Please be sure to check the ViewController Custom Class correct if the project is having multiple ViewControllers. And then make sure whatever the "collectionView.dequeueReusableCell(withReuseIdentifier: "ABC", for: indexPath) as?" matches with the identifier used in Collection Reusable View inside the Collection View.
If you are using storyboard instead of xib, here are few steps.
Making sure you fill the right identifier of your cell.
In the ColletionViewDelegate.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "YourCellName", for: indexPath)as! YourCellName
return cell
}
That's it. You also don't want to add registerClass:forCellWithReuseIdentifier: which will cause element nil error.
You need to give correct reuseble identifier in storyboard, which you have give in the code while registering the collectionViewCell.
Here is the code.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ClctnVC", for: indexPath)as! ClctnVC
return cell
}
What fixed this for me was (roughly) same as Omar's answer, except I ended up creating a new UICollectionViewCell custom class and giving the Cell Reuse Indentifier a new / different name than the one that I had used elsewhere in the application. It worked immediately after that.
Just in case, if you are working with storyboard set collectionView identifier in the right place in the Attributes Inspector -> Identifier field. Not under the class name in "Restoration ID".
If you are using collection view in tableView cell, add delegates to tableView cell not in the tableViewController.
I had the same problem.
When I created a CollectionViewController, the default reuseIdentifier was Cell with capital C, but I made a mistake and made my cell identifier in Storyboard to be cell
They must be the same.
If you ever try solution in answer another in issue not work but in code is correct syntax ,correct outlet UI.
You should check in you tableview or collectionview then check you maybe forget set delegate "tableview.delegate = self or collectionview.delegate = self"
collectionview.delegate = self
collectionview.dataSource = self
In my case is only you design collection overlap collection in your cell.
thank you.
I know this is an old one, but I've experienced the same problem and wanted to share what fixed it for me.
In my case, I've declared a global identifier
let identifier = "CollectionViewCell"
and had to use self right before using it:
collectionView.dequeueReusableCellWithReuseIdentifier(self.identifier, forIndexPath: indexPath) as! CollectionViewCell
Hope this helps someone :)
If you create it by code, you must create a custom UICollectionViewCell, Then, init a UICollectionView ,and use loadView to set the view is the UICollectionView that you create. If you create in viewDidload() , it does not work. In addition, you can also drag a UICollectionViewController, it saves a lot of time.