My custom cell contains the toggling badges and a label inside that badge (both in xib). Then how can I reuse the cell? If I make the cell.imgBadgeImageView.image = nil and cell.lblBadgeLabel.Text = nil in prepareForReuse method of custom cell both of them will disappear from all the remaining cells (as we are reusing the cells).
Do I need to add the badge and label inside it as a subview to the cell from code? If I do so how can I access the cell badge image view and badge label. I need to access these two because there is a nice animation (for the image view) and text change (for the label).
I am currently making all the cells nil which are not in the view using this delegate method:
-(void)tableView:(UITableView *)tableView didEndDisplayingCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
if ([tableView.indexPathsForVisibleRows indexOfObject:indexPath] == NSNotFound){
VBMerchantDealCell *cell = (VBMerchantDealCell *)[tableView cellForRowAtIndexPath:indexPath];
cell = nil;
}
}
I understand that you want to reuse the cell. I don't know if the approach that you are taking is right, but I do it the way Apple has told me to do it.
a. Set the reuse identifier in the xib. Say "MyCustomCell".
b. Register the cell after your table view allocation using:
self.cellNib = [UINib nibWithNibName:#"MyCustomCell" bundle:nil];
[self.tableView registerNib:self.cellNib forCellReuseIdentifier:#"CustomCell"];
c. Get the cell at your ``cellForRowAtIndexPath` using:
NSString *identifier = #"MyCustomCell";
UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:identifier];
Related
I have a UITableView embedded inside a parent UIView. I have a CustomUITableViewController class set as delegate and datasource for the tableview.
After a certain background operation, I get an updated array of objects to be displayed in the tableview.
When I update the datasource array and call tableview.reloadData method, the tableview doesn't refresh. It only refreshes if I scroll the tableview.
However, if I call the API as follows:
tableview.beginUpdates -> tableview.reloadSections -> tableview.endUpdates,
it works perfectly and immediately reloads the table.
The problem is that depending on the new data, I have to add a new section, or remove an old section from the tableview.
Hence I am not able to use the reloadSections API.
Any thoughts on how to fix this?
Code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellID = #"tempCell";
CustomCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID];
if (cell == nil) {
cell = [[CustomCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellID];
}
[cell initializeWithModel:modelsToShow[indexPath.row]];
return cell;
}
-(void) showModelsInList:(NSMutableArray*) models {
[modelsToShow removeAllObjects];
[modelsToShow addObjectsFromArray:models];
[self setupDataForList];
[self reloadTable];
}
-(void) reloadTable {
[self.tableView beginUpdates];
NSMutableIndexSet* index = [[NSMutableIndexSet alloc]init];
[index addIndex:0];
[self.tableView reloadSections:index withRowAnimation:UITableViewRowAnimationFade];
[self.tableView endUpdates];
//[self.tableView reloadData]
}
The showModelsInList method is invoked from the other class, in the main thread itself.
The modern way to initialize table view cells is to register the cell class (or nib, if the cell is defined in its own nib). viewDidLoad is a good time to do this...
// if the cell is a prototype defined in the nib containing the table view, or if
// the cell is built in code in its init method
[self.tableView registerClass:[CustomCell self] forCellReuseIdentifier:#"tempCell"];
// or, if the cell is defined in its own nib
UINib *nib = [UINib nibWithNibName:#"your cell's nib name goes here" bundle:nil];
[_tableView registerNib:nib forCellReuseIdentifier:#"tempCell"];
In either case above, the cell must have it's "tempCell" identifier initialized in IB or in code. Then, in cellForRowAtIndexPath, dequeue the cell using the method...
CustomCell *cell = (CustomCell *)[tableView dequeueReusableCellWithIdentifier:#"tempCell" forIndexPath:indexPath];
No further check is required to see if (cell == nil). This version of dequeue will just work (or crash, if something's not setup correctly).
I think, technically, it's a bug, but the truth is that, though it's not documented, you shouldn't be recreating subviews in cellForRowAtIndexPath when reusing cells.
Create the cells with all needed subviews at design time in Interface Builder. Changing their positions, sizes, and other properties in cellForRowAtIndexPath is okay.
If your cells have different subviews, each cell "type" should be its own class. Create a different prototype cell class with a different identifier for each, and simply use that identifier when you dequeue the cell. That way, you have the proper cell class in cellForRowAtIndexPath.
To reference additional properties (subviews) from your view controller, simply create class files for each cell type (derived from UITableViewCell). Assign it to the prototype UITableViewCell in IB, drag the views to the .h file to create outlets like you do for a view controller, then import that class in your view controller.
So, you might end up with code like this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
if (whatever) {
MyBasicCell *cell = [tableView dequeueReusableCellWithIdentifier:#"basicCell"];
cell.specialLabel.Text = ...
return cell;
} else {
MyOtherCell *cell = [tableView dequeueReusableCellWithIdentifier:#"otherCell"];
cell.otherLabel.Text = ...
return cell;
}
}
I would like to customize a simple UITableViewCell so that I run the customization only once and add values (e.g., cell title) later. My app's cell is more complex - it has subviews and uses auto layout; however, a simple example, I believe, will help in focusing on the objective.
I am using iOS 8, Xcode 6.X, Objective-C and Nibs (no storyboard) to keep it simple. I have not created a custom class for UITableViewCell. Instead, I have the following code:
- (void)viewDidLoad {
[super viewDidLoad];
//[self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:#"Cell"];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1; //FIXED VALUE FOR EXAMPLE'S SAKE
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 3; //FIXED VALUE FOR EXAMPLE'S SAKE
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSLog(#"tableView:cellForRowAtIndexPath:");
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
//UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
if (cell == nil) {
NSLog(#"cell == nil");
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
//CUSTOMIZING CELL THAT I WANT TO RUN ONLY ONCE
cell.backgroundColor = [UIColor redColor];
}
NSArray *numbersArray = #[#1,#2,#3];
cell.textLabel.text = [NSString stringWithFormat:#"%#", numbersArray[indexPath.row]];
return cell;
}
Which outputs:
tableView:cellForRowAtIndexPath:
cell == nil
tableView:cellForRowAtIndexPath:
cell == nil
tableView:cellForRowAtIndexPath:
cell == nil
FIRST QUESTION: Why is cell == nil run 3 times? It seems wasteful to run the customization code cell.backgroundColor = [UIColor redColor]; 3 times.
Now, when I enable:
[self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:#"Cell"];
And use:
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
Instead of:
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
I get the output:
tableView:cellForRowAtIndexPath:
tableView:cellForRowAtIndexPath:
tableView:cellForRowAtIndexPath:
SECOND QUESTION: Why isn't cell == nil run at all?
FINAL QUESTIONS: How can I make cell == nil run only once so that I format the UITableViewCell only once? Is there a better way to customize a simple cell, running the customization code only once?
Why is cell == nil run 3 times? It seems wasteful to run the customization code cell.backgroundColor = [UIColor redColor]; 3 times.
The table view most likely displays three cells at once, hence requiring three distinct cell objects.
Why isn't cell == nil run at all?
The documentation states that -dequeueReusableCellWithIdentifier:forIndexPath: always returns a valid cell if you registered the identifier previously. It basically takes care of checking if a new cell is required for you.
How can I make cell == nil run only once so that I format the UITableViewCell only once?
You don't. You will have to customize every single instance. I would recommend to use a custom subclass though, rather then messing with UITableViewCell from the outside.
The best way to do this, is to create a custom class for your cell, and do any customization that isn't dependent on the indexPath there. Usually, I do this in initWithCoder or awakeFromNib. You should register the nib in viewDidLoad; I don't see anything wrong with the code you mention in your comment to Christian's answer, unless the name of the file is wrong. It really isn't the view controller's business to be adding subviews or customizing your cell; that code belongs in the cell's class.
BTW, this doesn't keep the customization code from running multiple times. It needs to run once for each cell instance that you create, just like it does in your original code. The number of cells created will be equal to the number that fit on the screen at one time (plus one maybe).
i have designed a pop over (tableview style) for ipad. Now like in asp.net i want a option at the end, so that if user clicks it he can enter value into it. Can it be done?If so how? I have played with tableviewcellstyles and got a text box as well but for all options. If anybody has done, can you give me some ideas. Thanks.
EDIT : I should have mentioned i am using Monodevelop to develop this app.
One approach, assuming you're using a Storyboard to create view and set properties:
In your UITableView object properties set "content" property to "Dynamic Prototypes".
Then add an additional Table View Cell to your table from the storyboard editor's UI object list (just like adding another label or button or whatever).
You should now have two TableViewCell objects in your table view. If not, add another.
Give both table view cell objects different identifiers (that is, set property "identifier" for each to different strings).
Set up the first cell to display your normal content.
Set up the second cell to display your unique "last row" layout.
In your code, in cellForRowAtIndexPath return the "main" cell for most rows, but the alternate cell for the last row:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// rows are 0-indexed
if (indexPath.row == myDataSource.count) {
NSString *CellIdentifier = #"AlternateCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
// populate contents...
}
else {
NSString *CellIdentifier = #"NormalRow";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
// populate contents...
}
}
For that to work, you'll have to tell the TableView that there's one more row than you have actual data for:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return myDataSource.count +1;
}
I'm using storyboard with UITableView in UINavigationController.
In this UITableView, used custom tableViewCell having interior properties.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
CustomTableViewCell *cell = nil;
if (SYSTEM_VERSION_LESS_THAN(#"6.0") ) {
//iOS 6.0 below
cell = [tableView dequeueReusableCellWithIdentifier:#"Cell"];
}
else {
//iOS 6.0 above
cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath]; //work segue
}
Above code work well with push segue. But not when I used
cell = [[CustomTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"Cell"]; //not work segue
I used this alloc method for preserve cell's data from reusing cell.
It's just alloc vs deque.. method difference. What am I missing?
edit) I know that not using the dequeReusableCell method is bad for the performance reason. But, the number of cells would not be many. This is why I don't need the deque method.
"not working" means "do not perform push segue", not crash.
It shows cell same like when dequeReusable method used except the disclosure indicator icon at the right of cell. The indicator icon come from storyboard setting.
And when I touch the cell, the cell highlighted blue but the push segue does not performed.
CustomTableViewCell has 4 properties. That's all different from UITableViewCell. Users set the properties at DetailViewController(push segue lead to this). The cell doesn't have IBOutlet ref. In MasterViewController(having the tableView), cellForRowAtIndexPath method returns CustomTableViewCell above code.
cellForRowAtIndexPath method adds a on/off button on the left of indicator on CustomTableViewCell
And set a tag number for the cell.
The use of dequeueReusableCellWithIdentifier is what enables you to use your prototype cell. If you use initWithStyle instead of dequeueReusableCellWithIdentifier, then you don't and you therefore lose any segues, disclosure indicators, other UI appearance that you've defined for those cell prototypes, too.
If you're determined to go this route, you'll have to go "old school" (i.e. do what we all used to do before cell prototypes) and write your own didSelectRowForIndexPath. But if you already have that segue defined, let's say you called it "SelectRow", then your didSelectRowForIndexPath can perform that:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
[self performSegueWithIdentifier:#"SelectRow" sender:cell];
}
If you need to have your disclosure indicator, then your custom cell routine (or the cellForRowAtIndexPath) will have to set that manually. And if you add it with
cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;
Then you need to manually handle it:
- (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
[self performSegueWithIdentifier:#"SelectAccessory" sender:cell];
}
Bottom line, you can get this to work, but you're just doing a lot of extra work and losing losing the performance and memory benefits of dequeuing cells. I'd heartily encourage you to revisit the decision to not use dequeueCellWithIdentifier.
this is Nsr (just a beginner in xcoding), using xcode 4.3.3,
I've made a Custom UITableview with Custom UITableviewcell through storyboarding,
I have a UIBUtton and a UILabel in my custom cell.
i'v remove the cell selection (also cleared the background) so that only the buttons can be accessable which works as a backgound for the custom UILabel.
Now there are bunch of buttons since of using data array, and when i click any button, it segues to the other view (detail view), all i wanted is to set the custom Label (set over the detail view) from the previous selected button with "Label"...
means new Label = previous page's clicked Label from the custom tableview..
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
Custom *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[Custom alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
cell.myLabel.text = [myArray objectAtIndex:indexPath.row];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
[self.myTable reloadData];
}
I hope you get my problem, very sorry for my poor english..
Please help me, im in a real mess, coz this problem already took my 3 days :(
Thanx in advance, Cheers.
function return Custom cell but in header you write UItableviewCell change this to Custom