I have a UITableView which contains so many rows.In my screen there can be UISwitch added for few rows in UITableView.Please tell me how can i do this?
Should i create a custom cell with a UISwitch & show or hide the UISwitch.
Should i add the UISwitch directly from the code in cellForRowAtIndexPath:.
Any suggestions please?
EDIT:
I have tried this code but this does not work.
SettingsCell *cell;
static NSString *CellIdentifier = #"SettingCell";
if (cell == nil)
{
cell = [tableView
dequeueReusableCellWithIdentifier:CellIdentifier
forIndexPath:indexPath];
[cell setSelectionStyle:UITableViewCellSelectionStyleDefault];
[tableView setSeparatorStyle:UITableViewCellSeparatorStyleSingleLine];
}
if(indexPath.row==2 || indexPath.row==3)
{
cell.switch_value.hidden=false;
}
else
{
cell.switch_value.hidden=true;
}
cell.label_text.text = [VALUES objectAtIndex:indexPath.row];
return cell;
If you are creating a new cell then you should use that cell for only required rows. For rest of the rows you should not take pain of hiding the switch.
If you create UISwitch directly from the code in CellForRowAtIndexPath, then you should take care while re-using the cell. If you use different identifier for both cell types, the show/hide problem will get resolved.
Both solutions work here : you can add an UISwitch in a UITableViewCell in cellForRowAtIndexPath: : be just careful to check that no previously created UISwitch exists before creating a new one.
The second solution consists in using a custom UITableViewCell subclass.
I prefer this solution because you don't have to cast your subviews or use viewWithTag: method, or even to check if your custom subview was already created.
You should create some simples subclasses, such as PickerCell, SwitchCell, TextFieldcell : thus you will have your own set of custom UITableViewCell subclasses ready to be used within all your projects.
Assuming you have registered your reuse identifier, try this :=
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *SettingCellIdentifier = #"SettingCell";
static NSString *StandardCellIdentifier = #"StandardCell";
if (indexPath.row == 2 || indexPath.row == 3) {
SettingsCell *cell = (SettingsCell *)[tableView dequeueReusableCellWithIdentifier:SettingCellIdentifier forIndexPath:indexPath];
[cell setSelectionStyle:UITableViewCellSelectionStyleDefault];
[tableView setSeparatorStyle:UITableViewCellSeparatorStyleSingleLine];
cell.label_text.text = [VALUES objectAtIndex:indexPath.row];
// Do whatever you want with the switch here
return cell;
}
else
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:StandardCellIdentifier forIndexPath:indexPath];
// instantiate a regular UITableViewCell
cell.label_text.text = [VALUES objectAtIndex:indexPath.row];
return cell;
}
}
Related
I am doing using some code that I have seen work before. Essentially a user answers yes or no on a post with some buttons. Pressing yes or no updates the database, which is working correctly, and it also updates the visible UI, which is not working. This UI updates the buttons so they one is selected, other is highlighted and both are disabled for user interaction. Also it makes changes to two UILabels. The method that these buttons calls needs to update the database and retrieve the buttons from the tableViewCell and update the changes I have the methods working in another ViewController so I can not understand the difference here. Here is my cellForRowAtIndexPath
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *simpleTableIdentifier = [NSString stringWithFormat:#"%ld,%ld",(long)indexPath.section,(long)indexPath.row];
NSLog(#" simple: %#",simpleTableIdentifier);
if (indexPath.row==0) {
ProfileFirstCell *cell = [self.tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil)
{
cell = [[ProfileFirstCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:simpleTableIdentifier];
}
cell = [self createProfileCell:cell];
return cell;
}else{
YesNoCell *cell =[self.tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell==nil) {
cell=[[YesNoCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier];
}
cell = [self createYesNoCell:cell:indexPath];
return cell;
}
}
Essentially what this does is create the users profile in the first cell, and load all the questions that user asks. This is the major difference I see between the old tableView and this tableView. In createYesNoCell I create the UIElements and create tags as follows
cell.yesVoteButton.tag=indexPath.row+ yesVoteButtonTag1;
cell.noVoteButton.tag=indexPath.row+ noVoteButtonTag1;
cell.yesCountLabel.tag=indexPath.row+ yesCountLabelTag1;
cell.noCountLabel.tag=indexPath.row+ noCountLabelTag1;
The buttons have the selector that initiates a number of things. It finds which button was pressed by the following.
NSInteger index;
if(sender.tag>=yesVoteButtonTag1){
NSLog(#"Yes button pressed");
votedYes=true;
index=sender.tag-yesVoteButtonTag1;
}else{
NSLog(#"No button Pressed");
votedYes=false;
index=sender.tag-noVoteButtonTag1;
}
UILabel *yesLabel = (UILabel*) [self.tableView viewWithTag:index+yesCountLabelTag1]; // you get your label reference here
UIButton *yesButton=(UIButton *)[self.tableView viewWithTag:index+1+yesVoteButtonTag1];
NSLog(#"Tag IN METHOD: %ld",index+yesVoteButtonTag1);
UILabel *noLabel = (UILabel*) [self.tableView viewWithTag:index+1+noCountLabelTag1]; // you get your label reference here
UIButton *noButton=(UIButton *)[self.tableView viewWithTag:index+noVoteButtonTag1];
These viewWithTag calls are nil when I look at them. The only difference that I can see from my earlier implementation is that the old one had sections and one row, while this one is all rows and one section. So replacing the indexPath.section with indexPath.row should account for that. Also I checked that the tag made in cellForRowAtIndexPath is the same as the row recovered in the yes/no vote method, because it is displaced by one because of the profile cell being created at indexPath.row==0. I tried passing the cell to the yes/no vote method and tried to recover the buttons and labels with contentView as some suggestions made on similar posts. However this didn't seem to solve my problem. Really would appreciate some insight on this.
have you call the '[tableView reload]' method to update the UITableView, it may helps.
Firstly, the table reuse identifier should be used for types of cells, not one for each cell. You have two types, so you should use two fixed reuse identifiers.
ProfileFirstCell *cell = [self.tableView dequeueReusableCellWithIdentifier:#"ProfileCell"];
if (cell == nil) {
cell = [[ProfileFirstCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:#"ProfileCell"];
}
and
YesNoCell *cell =[self.tableView dequeueReusableCellWithIdentifier:#"YesNoCell"];
if (cell==nil) {
cell=[[YesNoCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"YesNoCell"];
}
Secondly, rather than trying to get a reference to a cell after creating the table, which isn't working for you, you should initialize the cells completely when they are created. (TableView won't create cells unless they're visible, so you shouldn't rely on their existing at all.)
createProfileCell should really be called initializeProfileCell, because you're not creating the cell in it - you already did that in the line above, or recovered an old one.
Then your call to initializeProfileCell can take a flag specifying whether it is a Yes or No cell and set its properties accordingly.
cell = [self initializeProfileCell:cell isYes:(indexPath.section==0)];
Similarly with createYesNoCell --> initializeYesNoCell.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"YOURCELL_IDENTIFIER";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
UILabel *title = (UILabel*) [cell viewWithTag:5];
UILabel *vensu =(UILabel*) [cell viewWithTag:7];
vensu.text = #"YOUR TEXT";
title.text = #"YOUR 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 a custom UITableView class that I have multiple instances of in a single ViewController. What is the most elegant way to populate these different instances with unique cell data? Thanks for any help in advance!
Right now I can think of two possible solutions:
Have different table view data sources for each table. You can create the data sources in the same file as the view controller if that is important to you.
Have your tableView:cellForRowAtIndexPath: method conditionally load the cells depending on the table. You can find out which table view is calling the method with the first argument of the method. You could also use the tag property of UITableView for differentiation.
I personally prefer the first one.
The answer I wrote here:
https://stackoverflow.com/a/19568737/480415
May help you achieve this.:
You could also put 2 separate UITableViews on your UIViewController,
then handle it in the delegates/datasource methods, ie:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if(tableView == _leftTableView)
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
//fill cell data here
return cell;
}
else if(tableView == _rightTableView)
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
//fill cell data here
return cell;
}
return nil;
}
Notice that the method signature tableView:cellForRowAtIndexPath: includes a reference to the tableView. You can use that to determine which tableView is requesting a cell, and return the same/different data accordingly.
in your controller, you should have your tableViews defined as a properties
#property (nonatomic, strong) UITableView *myTableView
Then, in tableView:cellForRowAtIndexPath: you can check which table is making the callback using something like:
if (tableView == self.myTableView){
//return cell
} else if (tableView == someOtherTableView) {
//return some other cell
}
I'm using ARC but it seems that my custom UITableCellView is not release.
TBMListingLineView is a subclass of TBMGlobalCustomCell which is a subclass of UITableCellView.
In TBMListingLineView there are 10 UILabels (nonatomic, retain)
I've implemented in both classes the method dealloc which is never called (breakpoint doesn't stop the execution)
When I'm scrolling the TableView, the number of UILabel is increasing in Instruments/Allocations and that causes the application crashed after several memory warning.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
static NSString *CellIdentifier = #"Cell";
TBMGlobalCustomCell* cell;
cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
switch(sortIndex) {
case 0 :
cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil || ![cell isKindOfClass:[TBMListingLineView class]]) {
cell = [[TBMListingLineView alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
break;
....
return cell;
}
The first problem is that you call dequeueReusableCellWithIdentifier twice for each cell.
And then you "throw away" the second dequeued cell also if it does not have the right class.
A better solution is to use different cell identifiers for each cell (sub)class used in
the table view, so that dequeueReusableCellWithIdentifier returns instances of the correct
class.
I'm doing a simples app using Storyboard that a have a View with a UITableView with a UITableViewCell that do the navigation to another UIView.
So a have to code to populate the cell on the table view.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"SampleCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellIdentifier];
NSLog(#"cai no init da cell");
}
GPItem *item = [self.items objectAtIndex:indexPath.row];
cell.textLabel.text = #"Post";
cell.detailTextLabel.text = item.imageURL;
return cell;
}
I realised that the code if (cell == nil) { ... never executes so I really need to do that on uses the cell from Storyboard?
Thanks.
You are correct; that code is guaranteed to return a non-nil cell if you are using a storyboard. Also, in iOS 6, the new call dequeueReusableCellWithIdentifier:forIndexPath: never returns nil. See the discussion in my book:
http://www.apeth.com/iOSBook/ch21.html#_registering_a_cell_class
If you've declared your UITableViewCell in table view's prototype cells it's already allocated and just needs to be dequeued. If you're using a custom UITableViewCell subclass, then you must check if it's nil and allocate new entities when necessary.
Nope you don't need that code when using a cell made in your storyboard.
It is probably best to remove this code so that you crash nice and early if the identifier you gave to the cell in interface builder and the identifier you use in code ever drift. This snippet will mask this error and just provide a cell that you most likely was not intending to have.