iPad App - how to change UITableViewCell style? - ipad

Again I'm moving from Xcode 3 to 4 and finding that some things are different. I'm writing a splitview iPad app and working from all the code that was generated for me. In the master view I want to customize the UITableViewCell to use the UITableViewCellStyleSubtitle style. In the past that was simple and I knew it was done within tableView:cellForRowAtIndexPath:.
In the new template code there is no [[UITableViewCell alloc] init....] there is only
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell"];
so I can't modify the style here. I looked in the storyboard and found the cell but cannot seem to change the property there either.
In short, I can't find where the UITableViewCells are alloc/init'ed so that I can make my change. Where is this done in Xcode 4?

If you're using the storyboard-based template, the cell is created by the storyboard, so you need to configure it in the storyboard in IB. If you haven't found that, it's here:
Select the cell, and it'll be in the attributes inspector. While you're there you might want to set a reuse identifier you can use in tableView:cellForRowAtIndexPath:.
(You can also use IB to change the font or other properties of the cell's textLabel and detailTextLabel and set accessory styles like you would have before in tableView:cellForRowAtIndexPath:.)

UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil];
This is still done in cellForRowAtIndexPath

Related

Create Table View Style UITableViewStyleCustom

As everybody knows, there is easy to initialise UITableViewCell:
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"cell"];
But how to create my own table view style definition "UITableViewCellStyleCustom"?
As the other answers already pointed out, there are tons of tutorials as on how to create your own cell style.
However, remember the preferred way to create a table view cell is to reuse one in tableView:cellForRowAtIndexPath: like so:
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"myIdentifier" forIndexPath:indexPath];
EDIT
If you want to implement your own version of initWithStyle:reuseIdentifier:, you can obviously create your own enumeration of cell styles, e.g.:
typedef NS_ENUM(NSInteger, MyTableViewCellStyle){
MyTableViewCellStyleFile,
MyTableViewCellStyleFolder
};
Then you can define a custom init method like so:
-(instancetype)initWithMyStyle:(MyTableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier;
Apart from that, UITableViewCellStyleDefault is part of the enumeration UITableViewCellStyle in UIKit. So it is basically just an integer value and passing a different value than the defined will not work in default init methods.
There are a couple of solutions for this:
You prototype your own custom cell on a storyboard. This is a quick way of creating a custom cell without too much of a hassle. If I am not mistaken this is not available in NIBs.
Create your Cell in a NIB.
On both approaches, you will have to then subclass a UITableViewCell and add the behaviour you want to it. You will also need to specify on the Storyboard or Nib, that you want that cell to have the type of the subclass you just created. There a tons of tutorials on the web on how to do it.
First, you need to subclass UITableViewCell to get a custom cell.
When initializing in code (why?), just use UITableViewCellStyleDefault as the style parameter in the designated initializer. I recommend initializing the cell via storyboard or Interface Builder.

How do I do the UISearchDisplayController's tableView to use the prototype cell defined for the regular tableView

I have a tableViewController with a search bar and a search display controller.
That main tableViewController's tableView has a prototype cell defined on storyboard.
I am having crashes on the line
FileManagerTableViewCell *cell = (FileManagerTableViewCell *)[tableView dequeueReusableCellWithIdentifier:cellIdentifier];
when the search tableview is about to be displayed. Apparently the search tableView cannot access the prototype cells defined on the main tableView on storyboard.
How do I make the search tableview access the prototype cell defined for the main tableview on storyboard?
If you want both tables to use the same cell, you should design that cell in a xib file instead of in the storyboard. Have both tables register the nib, using
registerNib:forCellReuseIdentifier:.
The answer to this is simple to edit but complex to understand why Apple created something half-cooked like that. The solution comes from the first comment of this answer
The answer is to use self.tableview instead of tableview.
FileManagerTableViewCell *cell = (FileManagerTableViewCell *)[self.tableView dequeueReusableCellWithIdentifier:cellIdentifier];
The explanation: when a search starts, iOS switches to show the search tableview but that tableView has no prototype cells, so this line
FileManagerTableViewCell *cell = (FileManagerTableViewCell *)[tableView dequeueReusableCellWithIdentifier:cellIdentifier];
is asking iOS to dequeue a cell from the search tableView. By using self.tableview, you are saying to dequeue a cell from the main tableView, that contains a prototype cell defined on storyboard.
I hope one day iOS APIs are designed to make our lives easy, thing that is far from happening today.

how to fix an issue with custom subclass of uitableviewcell reuse?

Before I get crucified for this, let me state... Yes, I have read every "relevant" answer on this topic and have not found a workable solution. Most "correct" answers are pre-ARC and discuss "releasing" a cell, which just isn't done anymore. Secondly, my problem is not "global", meaning some views have no problems, while others do. So here is my question...
I have sub-classed uitableviewcell and setup some uilabels & custom uiviews. From there I wired everything up in ib (Xcode 5.x iOS 7.x). Once I put in the appropriate code and create the tableview & dynamic cells from a nsarray "not mutable" everything works exactly as expected with no issues.
This is the fun part. I am making changes to allow the data source of the tableview, which is an nsarray to be mutable to allow adding and removing of items / cells. This is where things get hairy. When I start to add more objects to the array and when the reuse cell is being put on screen visual data from old cells is being reused on new cells. I say "visual" because once the cell is selected the view updates to display the correct information. The part that is interesting is that as I stated I have some uilabels which never have any problems being redisplayed, my custom views however are now the piece of the puzzle that is displaying info from past cells, and when scrolling back up, the original cells no longer display the correct information. Once the cell is tapped, then the cell updates and displays the correct information.
the most confusing bit of this is that before my array was mutable and had a static amount of objects this worked fine. Even if a cell went off screen and came back, it was still the correct information being displayed. Now I know that shouldn't have anything to do with it, but it is strange that it worked using the same tableview & cell code that I am using now.
I have tried adding in
MyCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
if (!cell) {
cell = [[MyCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"Cell"]; // note: obviously as stated, tacking on "autorelease" here as mention in other suggestions is not going to work.
}
Which doesn't fix the issue.
I tried overriding the "prepareForReuse" method on my custom cell subclass and that does not resolve the issue either. I have made the views, "strong" & "weak", and all that and still every 3rd or so cell gets repeated with garbage data until it is refreshed. Again, the uilabels which are setup the same way as the views have no problems and data is never reused. I would say there is a problem with my custom views, but setting up the table from a static source of identical information there is no problem.
I would like to post some code, but it's all pretty generic code for tableviews & delegates. any suggestions would be greatly appreciated.
As i said the code is all pretty generic, but apparently it needs posing anyway so here it is..
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
MyThing *thing = self.stuffArray[indexPath.row];
MyCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
cell.thisLabel.text = thing.someText;
cell.thatLabel.text = thing.otherText;
cell.view1.someProptery = thing.object1.property
cell.view2.someProptery = thing.object2.property
cell.view3.someProptery = thing.object3.property
//"someProperty" on "view..." is an NSInt that is used to determine custom drawing in the view.
return cell;
}
I think the key to the solution lies in your comment about the custom views in the cells. If cellForRowAtIndexPath is altering the states of those views, they need to know that they must be redrawn, so you'll need to augment the synthesized setter in your custom view.m that has someProperty.
If the someProperty determines how this view get's drawn, then it's incumbent upon the setter to indicate that the view is out of date....
- (void)setSomeProperty:(NSInteger)someInt {
_someProperty = someInt;
[self setNeedsDisplay];
}

invalid nib registered for identifier (CELLNAME) - nib must contain exactly one top level object which must be a UITableViewCell instance

Sorry for the long title, but I wanted it to be clearly seen from a google search.
Also, this differs from many of the other similar questions on here as its not specifying a 'null' identifier but one that actually exists.
Essentially the problem occurs when your trying to navigate using one of the cells in a table view controller to segue to another view (Whatever that view may be).
Most people would run into this problem after gunning through the apple tutorial on the 'To-Do List' and expecting the cells to operate in the same motion regardless of their purpose.
This problem is probably simplistic to most but for a beginner, its quite hard, it took me around 3 hours.
Basically the error is:
invalid nib registered for identifier (prototypeCell) - nib must contain exactly one top level object which must be a UITableViewCell instance
Where 'prototypeCell' would be whatever your cell is called. This is an exception that occurs immediately as the app is launched.
I had the same problem as above but I wasn't using storyboards and the problem just appeared out of the blue.
I found that the solution was in the tableview cell file. I had added a uibutton, but it had been added outside the bounds of the cell by mistake. This meant it was almost like an extra view in the uiview.
Once I found it and deleted this extra view the problem disappeared immediately.
If you have having this error, check the uitableviewcell for extra views and objects added by mistake
The answer of simon_smiley pointed me to the right direction, but some more trial-and-error was needed leading to the following solution:
The problem doesn't only occur for additional top-level UIView objects, but also for gesture recognizers. So make sure not to use any gesture recognizers in your failing XIB file, you can set them up in code instead.
For example you can do it in awakeFromNib as noted by vahotm in the accepted answers comments.
I had the same problem! And in my case custom cell was the subclass of UIView by mistake instead of UITableViewCell. So replacing UIView with UITableViewCell fixed the problem!
Same problem because I drag and drop a UITapGestureRecognizer on the subviews of ContentView. Just remove it.
In my case, I had an extra ImageView inside the xib added by mistake. Removed it and everything worked perfectly.
The problem is that there are multiple cells in your storyboard that have the same name. Such as for a single table view, there are multiple cells that have the same identifier. In my case I had three cells that were all called 'prototypeCell'.
The fix is actually quite easy. For every cell, name it a simple name with the cell number at the end. This cell number has to match the indexPath.row later on, so basically start from 0 onwards.
For Example:
prototypeCell0
prototypeCell1
prototypeCell2
Then, go into the class thats responsible for the controller, and find the function
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
Then replace the code:
static NSString *CellIdentifier = #"PrototypeCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
With the code:
static NSString *CellIdentifier = #"ListPrototypeCell";
NSString* num = [NSString stringWithFormat:#"%d", indexPath.row];
NSString* actual = [CellIdentifier stringByAppendingString:num];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:actual forIndexPath:indexPath];
Everything else can stay the same. This code basically gets the row number, adds it to the identifier base, the gets the cell from the table using that string. So if its responding to a different row number, it return a different cell.
Just for clarification, my whole function for the problem is as follows:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"ListPrototypeCell";
NSString* num = [NSString stringWithFormat:#"%d", indexPath.row];
NSString* actual = [CellIdentifier stringByAppendingString:num];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:actual forIndexPath:indexPath];
cell.textLabel.text = self.content[indexPath.row];
return cell;
}
Good Luck!
Also, this is my first Answer/Question combo, so I really have no idea how to approach it, so if i've done anything wrong, please tell me and/or change it.
Thanks!
I'll put up my dumb solution for the sake of googlers...
This was my personal dumb mistake - when I created a new custom cell for my table view, I went to the top level directory of my project -> add new file -> and created an Empty File type under the OS X section rather than the iOS section.
Hopefully, most people have the other issue described above since it's less embarrassing :P
Sometimes you using storyboard and have collectionView inside it and collectionView as well. After that you decide to simplified your Storyboard and divide cell into another nib. You create empty nib, Ctrl+C from storyboard -> Ctrl+V into nib.
Everything looks fine but you'll have Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'invalid nib registered for identifier (PrettyCollectionViewCell) - nib must contain exactly one top level object which must be a UICollectionReusableView instance'
Ansver: do not do this. After I clean nib and add all of elements as I've in Storyboard - it fixed.
Seems like Bug of xCode IB - Version 7.3.1 (7D1014)
This error is mainly due to some extra views that are added by mistake. Have a look in the .xib file and check for extra unwanted view added by mistake. Delete that and it will work perfect. You can check for unwanted views by this is how it looks
In my case,
I have added UITableViewHeaderFooterView subclass and XIB in different target instead of actual target.
Make sure it is in running target.
I solved this by setting the class name in interface builder to match the cell reuse identifier (for that class) in interface builder.
Note: I'm not saying the class and identifier have to be the same. I'm saying they need to link the corresponding view and backing model.
I had this issue, finally found out that i had created UITableViewCell Subclass instead of CollectionViewCell subclass. it was evening, and i was tired,lol. Fixed it in the mng.
I dragged collection view cell from other project and got this.
'invalid nib registered for identifier (cell) - nib must contain exactly one top level object which must be a UICollectionReusableView instance' .
Then i compared the collection view cell from created my me and found reusable view missing. Hope this help.
It was a silly mistake on my side and I had two tableview cells which caused this error. Removed the other 'table view cell' which fixed the issue.

Replace the UIImageView with UILabel in a UITableViewCell

I want to create a UITableViewCell that is very similar to the standard UITableViewCell with the UITableViewCellStyleSubtitle. The user is able to select a cell and the a checkmark should be set on that selected cell.
I want to display a UILabel instead of the UIImage in the left part of the cell. I have tried to add a UILabel to the contentView. The UILabel is displayed above both the text and the detailed text label. I would like to move both the text and the detailed text to the right when my the UILabel expands to the right, just as with the UIImage.
I know to make a custom cell in Interface Builder, but then I do it I don't know how to get the checkmark image, the one that is used when setting the UITableViewCellAccessoryCheckmark. There is the disclosure available as a button (and several others), but the checkmark does not seems to be available. Is there a way to get the checkmark icon and use it in a custom cell in Interface Builder? I don't want to make my own checkmark.
I have searched the Internet and Stackoverflow, but I cannot find the answer to my question. I guess I am missing a crucial part, but I cannot figure this one out by myself.
Are there any free open source or creative commons replacements?
If I'm understanding everything correctly, it seems like the easiest thing to do would be to not use the built in text and detail text on the cell and customize those labels as well.
Then in your:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath adjust the width/offset of your labels as necessary.
For the checkmark, if you're not adjusting where the disclosure image appears you should just be able to set it with cell.accessoryType = UITableViewCellAccessoryCheckmark
If by "checkmark" you mean a box with a tick or similar that the user can switch on or off with a tap, then I'm afraid you're out of luck, as cocoa-touch contains no such control.
However, rolling your own is easier than you might think. You simply need a pair or png images for the graphics (one for 'checked' and one for 'unchecked'). There are thousands of free icons and buttons available on the web.
Then you can create a custom UIButton like this:
UIButton *checkmark = [UIButton buttonWithType:UIButtonTypeCustom];
[checkmark setImage:[UIImage imageNamed:#"unchecked"] forState:UIControlStateNormal];
[checkmark setImage:[UIImage imageNamed:#"checked"] forState:UIControlStateSelected];
You can then check the state of the button (whether it is checked or not) using the selected property. For more info on selected see the Apple documentation here.
Rather create a custom cell programmatically.
There is sample code here.
Also for the checkmark you can add
cell.accessoryType = UITableViewCellAccessoryCheckmark
to your
tableViewCellWithReuseIdentifier or in
cellForRowAtIndexPath
whichever will suit your app.

Resources