Using multiple identifiers in UICollectionViewCell - objective-c

I want to use multiple identifiers in UICollectionViewCell.
But it seems like I can set just one reuse identifier for CollectionView.
[collectionView registerClass:[CollectionViewCell class] forCellWithReuseIdentifier:#"CollectionViewCell"];
It really worked with just one identifier, but when I use the different identifier like this, it gives the error message.
CollectionViewCell *cell = (CollectionViewCell *)[collectionView dequeueReusableCellWithReuseIdentifier:#"NewID" forIndexPath:indexPath];
Terminating app due to uncaught exception
'NSInternalInconsistencyException', reason: 'could not dequeue a view
of kind: UICollectionElementKindCell with identifier
CollectionViewCell - must register a nib or a class for the identifier
or connect a prototype cell in a storyboard'
How can I set multiple identifiers in UICollectionViewCell?
I want to display multiple custom cells simultaneously.
Each cell has UIScrollView and UIPageControl.
Unless I can set different identifiers, the instance will be reused to the new cell and the UIPageControl are not reacted by the movement in the each UIScrollView.

You must call registerClass:forCellWithReuseIdentifier: for each class and cell you wish to use.
If you want to use cells with different reuse identifiers, you must create different classes for them and then register those classes with the collection view for that reuse identifier.
[collectionView registerClass:[FooCell class]
forCellWithReuseIdentifier:#"FooIdentifier"];
[collectionView registerClass:[BarCell class]
forCellWithReuseIdentifier:#"BarIdentifier"];
[collectionView registerClass:[ExampleCell class]
forCellWithReuseIdentifier:#"ExampleCell"];
Now, you can use a cell with any of those three identifiers.

Related

Registered NIB, still getting assertion error when I use dequeueReusableCellWithIdentifier

I have a custom UITableViewCell class that I want to use to create custom table cells. I created the custom table cell's xib as well as its header and implementation files, all called RTRepairOrderTableCell.m/.h/.xib.
My issue is that even though I set the reuse identifier of the table cell to RTRepairOrderTableCell inside of the .xib file and registered the xib inside of my table view controller, I am still getting assertion errors when it tries to dequeue or create a new cell for use.
Inside of my view (table) controller I have the following:
- (void)viewDidLoad
{
[super viewDidLoad];
//Load the nib file
UINib *nib = [UINib nibWithNibName:#"RTRepairOrderTableCell"
bundle:nil];
// Register this Nib, which contains the cell
[self.tableView registerNib:nib
forCellReuseIdentifier:#"RTRepairOrderTableCell"];
}
There are no errors here and it finishes viewDidLoad just fine.
Inside of my cellForRowAtIndexPath I have the following:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
RTRepairOrderTableCell *cell = [tableView dequeueReusableCellWithIdentifier:#"RTRepairOrderTableCell" forIndexPath:indexPath];
return cell;
}
According to every tutorial I've seen, this should work so long as I have the reuse identifier set properly inside the xib file and I register that xib inside of viewDidLoad in the view controller class that will display the table cells, so I am at a loss as to why I am getting
*** Assertion failure in -[UITableView _dequeueReusableViewOfType:withIdentifier:], /SourceCache/UIKit/UIKit-2935.138/UITableView.m:5413
Turns out I had a few orphaned UIImage objects inside the nib file but outside of the UITableViewCell area, so it was throwing errors saying that the UITableViewCell needed to be the topmost view.
I faced this and it was driving me crazy. But it was my fault. I had set identifier #"Abc" in Nib file and I was registering the same nib file with some other identifie #"Xyz". Removed the identifier from nib, left that part blank and it worked like a champ. :)
This might be a rare case, but due to some sloppy copy pasta two different UITableViewCell custom classes used in my table had the exact same Identifier specified in the Xib.
Switching one of these cells to have a different Identifier in the Attributes Inspector fixed my Assertion failures.
Check if in your .xib there are no UI component outside main layout:
Label is not accepted
In this example, Label is not accepted and cause the exception

UICollectionViewController multiview error

I have an issue with the UICollectionViewController. I start with the default view controller and i make a segue to a UICollectionView. When i click on the button which link to that collection i have that
error :
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'
and the signal seems to be throw from that lign :
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:indexPath];
What does it mean ?
From UICollectionView reference:
Before you call either of these methods, you must tell the collection
view how to create the corresponding view if one does not already
exist. For this, you must register either a class or a nib file with
the collection view. For example, when registering cells, you use the
registerClass:forCellWithReuseIdentifier: or
registerNib:forCellWithReuseIdentifier: method. As part of the
registration process, you specify the reuse identifier that identifies
the purpose of the view. This is the same string you use when
dequeueing the view later.
So you need to register a class or nib to be able to deuqeue one.
To register:
[collectionView registerClass:[MyCellClass class] forCellWithReuseIdentifier:#"MyCellIdentifier"];
To dequeue:
[collectionView dequeueReusableCellWithReuseIdentifier:#"MyCellIdentifier" forIndexPath:indexPath]

Two UITableViewCells in my XIB for iPad/iPhone

I am trying to use two differently formatted UITableViewCells depending on if it's an iPad or iPhone. Both the cells show the same information, but the iPad cell has half the height and double the width. So the information is shown on one line vs two lines.
In my cellforRowatIndexPath,
I am putting the code:
HistoryCell *cell;
if (self.isiPad) {
cell = [self.tableView dequeueReusableCellWithIdentifier:#"historyiPadCellType" forIndexPath:indexPath];
}
else{
cell = [self.tableView dequeueReusableCellWithIdentifier:#"historyCellType" forIndexPath:indexPath];
}
However it seems like if I have 2 UItableViewCells in the same XIB, it will give me an error:
invalid nib registered for identifier (historyCellType) - nib must contain exactly one top level object which must be a UITableViewCell instance'
Is there anyway to fix this?
Thanks!
The best way to use differetnt cell for iPad and iPhone it is using 2 nibs:
HistoryCell~iphone.xib and HistoryCell~ipad.xib
And you must register you nib:
[self.tableView registerNib:[UINib nibWithNibName:#"HistoryCell" bundle:nil] forCellReuseIdentifier:#"historyCellType"];
System automatically loads xib depending on current device.

Setting .reuseIdentifier on a UICollectionViewCell

I have a particular UICollectionViewCell that I want to instantiate myself, and add to a UICollectionView. In order for this to work, the UICollectionViewCell instance needs its .reuseIdentifier property to be set.
Normally, the class or Nib that describes the cell is registered with the collection view, and the collection view instantiates the cell with its .reuseIdentifier already set, with these methods:
- registerClass:forCellWithReuseIdentifier:
- registerNib:forCellWithReuseIdentifier:
However, since I am constructing this cell outside of the collection view, these do not apply.
When I create the cell myself, there appears to be no way to set its .reuseIdentifier (because it is a readonly property, and there are no init... methods that initialize it).
When .reuseIdentifier is not set, the UICollectionView throws an exception when the cell is added. This behavior is different from UITableView, where reuse identifiers were optional.
An easy workaround to set the collection view cell's reuse identifier is to embed it in a .xib file and use the Identifier box, then create an instance of the cell with
[NSBundle.mainBundle loadNibNamed:#"MyCellName" owner:self options:nil][0];
I can then pass the above-instantiated UICollectionViewCell and everything works fine.
...but that seems like a pretty silly and arbitrary hoop to jump through. Is there some other way to get this property set on the cell instance without the .xib-wrapper detour?
Update: Apple's documentation says this:
To simplify the creation process for your code, the collection view requires that you always dequeue views, rather than create them explicitly in your code.
...which is actually not true, because it doesn't require this (i.e., externally-instanced cells work fine as long as their identifier is set somehow, e.g. if loaded from a .xib), and it also doesn't "simplify the creation process for my code" in my particular use case (rather requires an extra file; further it would be messy to require that the collection view create these few complex one-offs).
But the above does seem to imply that the answer to the question is no: it's intentionally "difficult" to create a usable cell except by having the collection view dequeue it.
I'm sorry to say it, but I think you're going to have to accept the fact that UICollectionView is the sole Apple-branded factory for producing UICollectionViewCells. So if you have two collection views in which you want to display the exact same object, you're out of luck!
The good news is that if you have two collection view in which you want to display a cell that looks exactly the same as in the other, then you can accomplish your task. It's easy:
#interface MyModelClass : NSObject
// a bunch of data
#end
#interface MyCollectionViewCell : UICollectionViewCell
-(void)refreshCellWithObject:(MyModelClass *)object;
#end
Meanwhile, in whichever classes own the UICollectionView cv1 and cv2
[[self cv1] registerClass:[MyCollectionViewCell class] forCellWithReuseIdentifier:#"MyCollectionViewCell"];
and
[[self cv2] registerClass:[MyCollectionViewCell class] forCellWithReuseIdentifier:#"MyCollectionViewCell"];
And their data sources can look the same
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
MyModelObject *obj = [self theObjectIWant];
MyCollectionViewCell *cell;
cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"MyCollectionViewCell" forIndexPath:indexPath];
[cell refreshCellWithObject:obj];
return cell;
}
Use these two methods.
– registerNib:forCellWithReuseIdentifier:
– dequeueReusableCellWithReuseIdentifier:forIndexPath:
See apple's UICollectionView Class Reference

Why can't I define IBOutlets when using custom "prototype tableviewcells"

I have my own table view cell which is defined in my storyboard. I have also defined a custom UITableViewCell class for this special cell. So when I want to create an Outlet for my custom prototype cell I get an error that the Outlet cant be created.
Since this is not possible I have to do some ugly workarounds and use the tags in IB to reference the individual labels and buttons later on in my code.
I don't really see why this is not possible and I wonder if working with tags and [myCell viewWithTag:] is the best possible way to go here?
Because the outlet is a one-to-one connection between your controller and a specific item within the view. In the case of a prototype cell, it is simply a description of a cell that can have an arbitrary number of different items (i.e. rows in your table view). How would the controller know which item you are referring to (e.g. row 5 or 500)? That is why you are receiving the error message.
Lucas provided one method to refer to your connection via tags which works perfectly well.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"CellIdentInIB"];
UIImageView *img = (UIImageView*) [cell.contentView viewWithTag:1];
//img.image = ...
//Access you prototype cell here to alter its style, example:
[[cell layer] setCornerRadius:10];
[cell setClipsToBounds:YES];
return cell;}
I assume you are using dynamic prototypes - in the attribute inspector of the tableview in the storyboard there is an option to select "static cells" or "dynamic prototypes". You can do what you are trying to do if you select "static cells" as there is only one cell in your tableview at run time for each cell in the storyboard. Using this approach you will only be able to use the cells you create in storyboard i.e. you will not be able to select the number of cells in your code.