can someone please explain why you should use viewWithTag to get subviews (e.g. UILabel etc) from a cell in dequeueReusableCellWithIdentifier?
Some background info: I've got a custom UITableViewCell with a couple of UILabels in it (I've reproduced a simple version of this below). These labels are defined in the associated NIB file and are declared with IBOutlets and linked back to the custom cell's controller class. In the tableview's dequeueReusableCellWithIdentifier, I'm doing this:
CustomCell *customCell = (CustomCell *)[tableView dequeueReusableCellWithIdentifier:#"CustomCellId"];
if (customCell == nil) {
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"customCell" owner:self options:nil];
for (id oneObject in nib)
if ([oneObject isKindOfClass:[CustomCell class]])
customCell = (CustomCell *)oneObject;
}
customCell.firstLabel.text = #"Hello";
customCell.secondLabel.text = #"World!";
return customCell;
Everything works fine. However from the tutorials I've seen, it looks like when changing the labels' values I should be doing this instead:
UILabel *firstLabel = (UILabel *)[customCell.contentView viewWithTag:555];
firstLabel.text = #"Hello";
UILabel *secondLabel = (UILabel *)[customCell.contentView viewWithTag:556];
secondLabel.text = #"World!";
(The labels' tag values have been set in the NIB).
Can someone tell me which method is preferred and why?
Thanks!
viewWithTag: is just a quick and dirty way to pull out child views without having to set up IBOutlet properties on the parent, or even without having to create a UITableViewCell subclass.
For very simple cases this is an acceptable solution, that's what viewWithTag: was intended for. However if you are going to reuse that cell a lot or you want it to have a more developer-friendly interface then you will want to subclass and use real properties as in your first example.
So use viewWithTag: if it's a very simple cell you designed in IB with no subclass and with just a couple of labels. Use a cell subclass with real properties for anything more substantial.
I've realised that it's useful to retrieve elements using "viewWithTag" if the elements were added to the cell programmatically (i.e. not defined in a NIB and hooked-up via IBOutlets)—this prevents multiple labels etc. to be created for each instance of the cell.
For me , viewWithTag is a God given. First of all : treating all views in a loop like taskinoor said is really easy. Also , I personally prefer this way because if I take a look on the code and want to see what happens with a view , I simply search for the tag. It's used everywhere the view is handled. Opposed to the xib approach where you have to look in the code and xib too. Also , if you have an offscreen view in a xib , you might oversee it.
I found a lot of xibs made by other programmers that were FULL with lots and lots of views. Some hidden , some offscreen , couldn't tell which is which since there were all overlapping.
In those cases , I think xibs are bad. They are not easy to read anymore.
I prefer everything made in code.
But if you decide to work with tags, remember to avoid hard-coding any tag. Instead make a list of #define definitions to keep the code clean and readable.
I always hook subviews to properties of my UITableViewCell subclass via IBOutlets, as you have done. I can't think of any good reason to use viewWithTag.
From UITableViewCell Class Reference: "The table view's delegate in tableView:cellForRowAtIndexPath: should always reset all content when reusing a cell." Keep it simple, clear out the content view. This makes no assumptions about custom cell classes, no casts, no class inspection:
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellId];
if (cell != nil)
{
NSArray* contentSubViews = [cell.contentView subviews];
for (UIView* viewToRemove in contentSubViews)
{
[viewToRemove removeFromSuperview];
}
}
viewWithTag: allows styling without creating a custom subclass of UITableViewCell.
You can assign a tag and reuse identifier to a prototype UITableViewCell in Interface Builder, then dequeue and modify the view with that tag within the implementation of your UITableViewController, without creating a custom class for that cell or creating IBOutlets for the cell's subviews.
In some cases, the simplicity of a cell makes a custom class feel like overkill. viewWithTag: allows you to add custom text and image to a cell in the Storyboard, then set those customizations via code, without adding extra class files to your Xcode project.
Related
Current I am creating a prototype cell in storyboard and using this cell as a section header.
Inside tableView:viewForHeaderInSection: method, I am dequeuing the cell and returning it.
My section header cell has a UITextField and a UIButton in it.
When I tap on text field keyboard appears but as soon as focus is moved away from text field whole section header disappears.
This happens when I return the cell directly as section header view, but if I return a newly allocated UIView as section header view onto which cell is added as subview then everything works fine besides autoresizing masks.
Why header is disappearing?
I am not sure what could be the best thing todo here.
-(UIView *) tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
static NSString *CellIdentifier = #"SectionHeader";
SettingsTableViewCell *sectionHeaderCell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
//return sectionHeaderCell; // returning cell directly, section header disappears when focus is moved away from text field.
UIView * headerView = [[UIView alloc] initWithFrame:sectionHeaderCell.frame];
[headerView addSubView:sectionHeaderCell];
return sectionHeaderCell;//header view never disappears, but auto resizing masks do not work. Need to know how to set autoresizing masks to headerView so that it resizes correctly.
}
Prototype cell table views only allow you to design cells in the storyboard editor, not section headers and footers. Your attempt to use a UITableViewCell as the section header is a clever hack, but it's just not supported by the classes involved—UITableViewCell is not designed to be used for anything other than a table view cell. It could do a lot worse than the view disappearing or not being laid out correctly; UIKit would be well within its rights to fail an assertion, delete all the app's data, revoke your developer certificate, or set your house on fire.
If you want your code to function properly, your choices are to either create your section headers in code or to put them in a separate XIB file. I know that's not what you want to do, but those are the options you have.
I had the same issue and the fix was to return the cell's contentView like:
-(UIView *) tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
static NSString *CellIdentifier = #"SectionHeader";
SettingsTableViewCell *sectionHeaderCell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
sectionHeaderCell.myPrettyLabel.text = #"Greetings";
sectionHeaderCell.contentView.backgroundColor = [UIColor whiteColor]; // don't leave this transparent
return sectionHeaderCell.contentView;
}
And you get the same autolayouted results as before, but without the disappearing.
I am sure you can use UITableViewCell as a section header, because UITableViewCell is subclass of UIView, so according to LSP
“objects in a program should be replaceable with instances of their
subtypes without altering the correctness of that program.”
In iOS 8, it's simple really. Just design your header the same way you design your cell. Everything is the same, you can put custom class and don't forget to add reuse identifier.
When it comes to use it in the code, just return that cell in tableView:viewForHeaderInSection method.
Don't forget to implement tableView:heightForHeaderInSection if you want to use fix height or tableView:estimatedHeightForHeaderInSection if the height depends on the cell intrinsic size.
I have gone through the tutorial below and it works fine. My question is how do I add more than the two standard cells to the prototype cell?
http://thedarkdev.blogspot.co.uk/2013/09/web-service-apps-in-ios7-json-with.html
cell.textLabel.text = "Title Text";
cell.detailTextLabel.text = "Detail Text"
I am wanting to add another 4 labels and would like to lay them out using the storyboards.
Any ideas how to do this?
You can use a custom cell type and you'll be able to add as many labels as you want:
Create a empty UITableViewCell subclass that you'll use for this cell. Note, this subclass doesn't need any code inside its #implementation. We're only going to add outlets for its properties, and those will show up in its #interface, but the storyboard eliminates the need to write any code for the cell, itself.
Back in Interface Builder, go to the table view in your storyboard and make sure it has a cell prototype. (If it doesn't drag one from the object library on to the table view.)
Over on the "Identity" inspector panel on the right, set the base class of the cell prototype to be your UITableViewCell subclass as the cell prototype's "base class";
In the storyboard's "Attributes" inspector for the cell, set the cell "Storyboard identifier" to something you'll reference down in step 5 (I've used CustomCell here);
Set the cell "Style" to "Custom" rather than "Basic" or "Detailed":
add your labels to the cell.
I've added for labels to a single prototype cell here:
Use the "Assistant Editor" to show your code simultaneously with the storyboard. Select one of the labels you've added to the scene, change the code down below to be the UITableViewCell subclass you created in step 1, and you can now control-drag from the label to create IBOutlet references for the labels to the cell's custom subclass:
By the way, I'd advise against using IBOutlet names of textLabel or detailTextLabel (not only are they too generic, but it can get confused with the labels that appear in standard cell layouts).
Now your tableview controller can reference this subclass:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellIdentifier = #"Cell"; // make sure this matches the "Identifier" in the storyboard for that prototype cell
CustomCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];
// retrieve the model data to be shown in this cell
// now fill in the four labels:
cell.firstNameLabel.text = ...;
cell.lastNameLabel.text = ...;
cell.emailLabel.text = ...;
cell.telephoneLabel.text = ...;
return cell;
}
So while there are a couple of steps to go through here, the net result is that you can design whatever cell layout you want, and with this very simple UITableViewCell subclass, your cellForRowAtIndexPath is incredibly simple, just referencing the IBOutlet references you connected in Interface Builder.
So far, have read a few posts, such as this and this, but they have not really helped with my situation.
I'm creating a dynamic form for iPad using 'plain' style UITableViews. I have multiple different UITableViews on the page, so I defined a separate object to server as my datasource and delegate. I understand how to change the text of each cell using the datasource; however, I have no clue how to link the UITextFields in my prototype cells to an IBAction. I could figure out how to create a single IBAction for all textfields in my table, such that they all update the same data, but I don't know how to have each UITextField have a one-to-one correspondence with my datasource.
Here is my prototype cell:
and my code thus far:
-(UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"myPrototypeCell"];
UILabel *buildingNumber = (UILabel *)[cell viewWithTag:100];
buildingNumber.text = [#"Building " stringByAppendingString:self.dataSource[indexPath.row][#"buildingNumber"]];
return cell;
}
self.dataSource is an NSMutableArray of NSMutableDictionaries.
Any help whatsoever is appreciated.
I initially thought you were referring to IBOutlets so my previous answer is somehow wrong but the inherent idea is still the same.
You cannot have IBActions or IBOutlets from a prototype cell unless the cell is subclassed. You can do so if the cells are static though, not that it can help in your case. Subclassing the UITableViewCell is not too hard or too bad, in fact if in the future you want to speed things up on your TableView, that is one of the many ways to start.
This tutorial provides a few different options for dealing with information inside a table view cell:
http://mobile.tutsplus.com/tutorials/iphone/customizing-uitableview-cell/
I almost always use a UITableViewCell subclass to deal with outlets and actions inside the cell. But this should be a decision you make based on your own architecture.
Hope this helps!
you need single for all your textField.So do the following:
Get the text field as your are getting label
UITextField *yourTextField = (UITextField *)[cell viewWithTag:101];
[yourTextField addTarget:self action:#selector(clickTextField:) forControlEvents:UIControlEventEditingChanged];
clickTextField method will get invoke for every Text Field.
Hope this helps.
Edit: Forgot to mention, you can you set delegate of UItextField and get a notification in UITextFieldTextDidChange: delegate method
In my app I have a table view with 12 types of custom cells.
I designed each within the storyboard.
In general everything works fine - thanks to your tutorials :)
My problem is, that I have individual styles an additional data in each cell - therefore I don't want them to be reused.
How can I generate a individual custom cell from storyboard to provide it in the -
tableView:cellForRowAtIndexPath:indexPath?
Any suggestions?
Thanks
Dominic
Added:
newCell = [tableView dequeueReusableCellWithIdentifier:cellType];
cell = newCell;
[cell styleWithElement:element andResubItem:resubItem];
my problem is, I need another way to create a custom-styled cell from the storyboard than the quouted above - I have no XIB and the above way is the only way I know to create my cell.
Could it be a solution to create a celltype once the above way and then copy the cell? Is there a way to copy a cell-object?
You can generate a random cell ID each time so it won't be reused.
However, that would not be very good for scroll performance and memory consumption, so consider actually reusing the cells (i.e. come up with a way to replace data in them).
Edit:
you can always just copy the views from one to another, something along these lines:
UITableViewCell* myCell = [UITableViewCell alloc] initWithStyle:YOUR_CELL_STYLE reuseIdentifier:YOUR_RANDOM_ID];
for (UIView *view in newCell.subviews) {
[view removeFromSuperview];
[myCell addSubview: view];
}
You may also need to adjust the frame of myCell to be the same as that of newCell, and if newCell is an action target for any events (e.g. clicking a control element within the cell triggers some action defined in the cell class) you'll need to reassign those to the myCell, too.
Edit2:
To move actions you could do something like this:
NSSet* targets = [control allTargets];
for(id target in targets) {
NSArray* actions = [control actionsForTarget:target forControlEvent:UIControlEventTouchUpInside];
for(NSString* selectorName in actions) {
[newControl addTarget:target action:NSSelectorFromString(selName) forControlEvents:UIControlEventTouchUpInside];
}
}
Substitute your new cell for the target - and make sure it implements all the necessary selectors (or you can put a new selector). The code above will replace targets for UIControlEventTouchUpInside - you can use the ones you need instead (or use allControlEvents to enumerate the ones used by a control).
I am wanting to create a custom UITableView cell. I would like to know how to do this. I understand how to actually create it and write code for it, but how can i create 1 style and then when i have more cells added, i want the same style. How can i do this? Is there a way to create 1 custom cell and have all the other cells that i want to add later follow this cells style?Thanks for the help!
In my projects I'm implementing method that creates custom style programmatically. Also it is possible to make custom cell via IB and when you need just take custom cell from it.
Don't forget that if you will write your code correctly then your cells will be reused and that method will be called only for number of cells that are visible in your table view.
may be this can help you http://iphone-bitcode.blogspot.com/2011/06/custom-tableview-cell.html
Write a separate .h/.m/.xib for the cell, and in the .xib set File's Owner to the class you want multiple copies of it in (your table view controller class, most likely). Attach it to an IBOutlet you created in the table view controller for new cells.
Then, each time you want a cell, try and dequeueReusableCellWithIdentifier: on your tableView, and if that doesn't work (you have no reusable ones), make a new cell using your custom class by simply loading the nib file. It will automatically create an instance of the cell and attach it to your IBOutlet, and then just retain the cell and set the outlet back to nil for the next time you need to create a cell. Essentially, I mean this (I have an IBOutlet UITableViewCell *cellOutlet):
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *reuseIdentifier = #"CustomCell";
UITableView *cell = [self.tableView
dequeueReusableCellWithIdentifier:reuseIdentifier];
if (cell == nil) {
[[NSBundle mainBundle] loadNibNamed:#"MyCustomTableViewCell"
owner:self options:nil];
cell = cellOutlet;
self.cellOutlet = nil; // autoreleases
cell.reuseIdentifier = reuseIdentifier;
}
// configure the cell here
return cell;
}