How to disable UITableViewCell selection one by one? - iphone

I am creating a table view based iPhone application, Where i need to disable one by one entire table view cell.
The requirement is like -
Case-1 :-
Initially only first table cell row should be user interaction enable, rest should be disable. We can easily do this to make indexPath.row 0 enable in CellForRowAtIndexPath method.
Case-2 :-
If User will tap on First cell and when again he will come back then First cell will be disable and second cell should enable(rest table cell will be disable at this time after second cell).
Case-3 :-
Again if user will tap on second table cell and come back to this table then apart from Third cell, others should be disable and so on...
It means i need to disable one by one all the table cell in serial order.It is possible by setting index but it won't be the proper way to do this. So, can you please suggest me what condition i need to give for this ? Please suggest me for further proceeding.
Thanks.

Just maintain an instance variable which holds the cell number that should be enabled.e.g. enabledCell.
Initialize enabledCell to 0. In didSelectRow increment enabledCell and reload the tableView. In cell for row at indexPath only enable the cell if the indexPath is same as the enabledCell value.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
// write code to dequeue or create new UITableViewCell here
// then check if index is same as the cell that should be enabled
if(index.Path.row == enabledCell)
cell.userInteractionEnabled = NO;
else
cell.userInteractionEnabled = YES;
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
//Assuming you have number of rows stored in variable numberOfRows
enabledCell++;
if(enabledCell == numberOfRows)
enabledCell = 0;
[tableView reloadData];
}

There are many ways to do this (from now on, I'm a assuming a linear table view with n rows and only one section).
For example, you can add a BOOL enabled property to your cell model object. In your controller's didSelectRowAtIndexPath: check that property and do nothing if it's set to NO. If it's set to YES then navigate to the appropriate page and exchange the enabled property with the one at index position i + 1 (or position 0 if it's the last entry and you want to cycle).

Related

Populating UITableView row 0 static and the rest with NSArray

In one section of my app I have a UITableView which is working fine right now. I would like to set row 0 cell.textLabel.text to #"Some string". Once row 0 has been set I would then like to load the rest of the rows from an array. Currently on load my array populates the table view but I'm trying to set row 0 as a sticky. The closest example I can think of is a forum topic that is set to stay at the top. My array is constructed of returned data from a web service call.
It's been a while since I've messed with table views, and I'm having a blank on this one.
The table view is 1 section, and I get the rows by counting the elements in the array. Since I would like to create an additional cell (row 0) I would call [array count] + 1. I don't know if this approach is the best one which is why I'm reaching out to the community here.
Any insight or a shove in the right direction would be great at this point.
You're on the right track:
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [array count]+1;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell"];
if ([indexPath row] == 0) {
// Code for first
[[cell textLabel] setText:#"First cell"];
} else {
[[cell textLabel] setText:[array objectAtIndex:[indexPath row]-1]];
}
return cell;
}
If you want the top of your table to be "sticky", why not consider using that string as a section header or title? In this case, the header stays visible at all times until the next section (e.g. if you had two sections, that is) is fully on the screen.
In any event, in one of my current projects I'm required to do roughly the same thing that you're doing and I have a static string being returned in row 0 (which scrolls off the top of screen when the table view scrolls down).
And in my UITableViewDataSource method, I always add one for the static cell to the number of objects in my array and in my "cellForRowAtIndexPath:" method, I increment the row by one when the indexPath.row is not zero. And if it is zero, I return my static string.
And dark_knight provides some nice sample code that illustrates what I was describing to you. So +1 to him/her.

Save the state of a table view/ save the state of the table view text

I have a UITableView and i can add and delete cells. I also have two buttons on each cell to add and subtract 1 from the cells text. But when i go to a different page and then back to the table view page, all the cells text is set back to 1. But i want the cell's text to stay at the value that the user had set it to! Could someone help me? Im not sure what to do whatsoever. Thanks!
You will have to maintain an NSMutableArray (probably of NSIntegers) that saves the values of the cells. Then, whenever the user changes the value, update the value in the array. Also, display the cell label values by reading from the array. Sample codes below-
-(void)plusButtonClicked:(id)sender
{
//figure out the cell index which was updated using sender
//update the array entry
//[self.array objectAtIndex:index]++; self.array is the array you will maintain
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
cell.label.text = [NSString stringWithFormat: #"%d", [self.array objectAtIndex:indexPath.row]];
}
If you want the values to persist even after the app is terminated and restarted, consider using CoreData.

UITableViewCell selector setSelected:animated: gets called many times?

I've discovered a strange behavior with setSelected:animated: in my custom UITableViewCell class. I discovered that this function gets called multiple times if I click on a cell in my table. I am wondering if this is normal behavior or a bug in my code.
To help with debugging, I've modified the setSelected:animated: function in my custom UITableViewCell class implementation as such:
- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
[super setSelected:selected animated:animated];
// Configure the view for the selected state.
if (selected)
NSLog(#"Yes %X", &self);
else
NSLog(#"No %X", &self);
}
If I click on a cell in the simulator, here is what I get in the console:
2011-03-22 22:05:26.963 marketPulse[3294:207] Yes BFFFDDD0
2011-03-22 22:05:26.964 marketPulse[3294:207] Yes BFFFDE30
You would think that I would get only 1 entry, since I only clicked on 1 cell.
And if I click on a different cell after that:
2011-03-22 22:07:11.014 marketPulse[3294:207] No BFFFD890
2011-03-22 22:07:11.016 marketPulse[3294:207] No BFFFDD00
2011-03-22 22:07:11.017 marketPulse[3294:207] Yes BFFFDDD0
2011-03-22 22:07:11.017 marketPulse[3294:207] Yes BFFFDE30
If I click on the same cell 2 times in a row, I get more than 2 Yes:
2011-03-22 22:08:41.067 marketPulse[3294:207] Yes BFFFDDD0
2011-03-22 22:08:41.068 marketPulse[3294:207] Yes BFFFDE30
2011-03-22 22:08:41.069 marketPulse[3294:207] Yes BFFFDE30
The more times I click the same cell, the more Yes I will get, and if I click on a different cell after that, I'll get a lot of No
I put a breakpoint before the NSLog, and looking at the debugger, it seems that all the repeated calls are coming from the same object.
Here is a part of my tableView:cellForRowAtIndexPath: function so you can see how my cells are being treated:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *ContentCellIdentifier = #"newsTableCellContent";
UITableViewCell *cell;
//index of cell data in tableData
NSUInteger index = indexPath.row / 2;
...
//content of story
else if( [indexPath row] % 2 == 1 ) {
cell = [tableView dequeueReusableCellWithIdentifier:ContentCellIdentifier];
if (cell == nil) {
NSArray *topLevelObjects = [[NSBundle mainBundle]
loadNibNamed:#"newsTableCells"
owner:nil options:nil];
for (id currentObject in topLevelObjects) {
if ( [currentObject isKindOfClass:[newsTableCellContent class]] ) {
cell = currentObject;
break;
}
}
}
((newsTableCellContent *)cell).content.text = [[tableData objectAtIndex:index] description];
}
return cell;
}
Everything works fine so its hard to tell if the repeat calls to setSelected:animated: are intentional or not. If this is normal operation, I can make do with another method, but I would just like to know if this is suppose to happen or not.
Thanks
What's going on is simply that the UITableView keeps track of which cells are selected in the table.
Since cells are reused when you scroll through a large table view, the table view has to keep the list of selected cells separate. Not only that, but whenever it reuses a cell it has to set its selected property, because it may be using an old, invalid selected state from a previous incarnation.
When you tap a cell, several things happen: the previously selected cell is deselected (using setSelected:). The new cell is highlighted. It's de-highlighted (at least if you tap, instead of holding your finger down), and the setSelected: method is called because the new cell was selected. That's one.
The second call is a delayed perform call, possibly from a point where the table view didn't yet know what the final state of the table would be. This call goes to _selectAllSelectedRows, which, as the name suggests, calls 'setSelected:animated:' on all selected rows. That's the second call. The reason for this is most likely to address potential issues due to the the table view being in a "transition", but who knows.
Whether it's a bug or not is up for interpretation. A fix for the duplicate calls is to simply do:
if (self.selected == selected) return;
right before the call to super (you do not have to call super if self.selected == selected).
This is a normal behavior if you're using iPad. (it is only called once on iPhone).
In order to stop getting multiple "setSelected:YES" or multiple "setSelected:NO", all you have to do is this:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
Now, 1 click on any cell gives you:
1 entry of setSelected:YES animated:NO
1 entry of tableView: didSelectRowAtIndexPath:
1 entry of setSelected:NO animated:YES
So, calls are now stable regardless of what you do.
Ideally you should not be calling setSelected from anywhere in your code.
UIKit will take care of calling it.
If you want to show a cell/row as selected in cellForRowAtIndexPath method simply call
tableView.selectRowAtIndexPath(indexPath, animated: true, scrollPosition: .None)
for that specific indexPath.
Again never ever call setSelected explicitly unless you really mean to.
It should definitely be called when table is scrolled. Cells are reused, that means, if you scroll cells in invisible areas will be reused and reinitialized, including the call to setSelected, which is basically a lightweight property setter.
If you really want to see what's happening, add a NSLog to tableView:cellForRowAtIndexPath: which will log indexPath and the returned cell.
The entire log should give you a good understanding what happens inside and why.
I suppose it will be something like this (Clicked on IndexPath 1:1)
Give me cell on 1:0 (previously selected cell).
Deselect 1:0
Give me cell on 1:0 again (updated after deselection)
Deselect 1:0 (update selected flag on this cell and trigger animation)
Give me cell on 1:1
Select 1:1
Give me cell on 1:1 again (updated after selection)
Select 1:1 (update selected flag on this cell and trigger animation)
Clicking on a selected cell again is only slightly different - instead of triggering unselecting, it triggers another update.

Referencing UISwitches within UITableCells

I have a table view which I'm using for some settings in my app. The tables cells are all default (no customisation at all), and simply contain some text for their label and a UISwitch for the accessory view.
My problem is that I need a reference to the switch so that I know when it has been switched on and off.
Currently I am setting the 'tag' property of the switch to be that of the cell's index within the table (grabbed from [indexPath row] in tableView:cellForRowAtIndexpath:).
This is fine when you only have one Section in your table, but I am now adding a new section. The problem is that they are both 0 based indexed so the switches in each section will end up reusing the tags - which isn't good.
Any suggestions on a better way to achieve this?
Thanks.
If you know roughly how many sections and rows you will have, like oh, say, not more than 1 million rows per section, just hash the section and row like this:
const int oneMillion = 1000000;
int tag = (section * oneMillion) + row;
slider.tag = tag;
Then, to figure out the section and row, reverse the logic:
int tag = slider.tag;
int row = tag % oneMillion;
int section = tag / oneMillion;
NSIndexPath *indexPath = [NSIndexPath indexPathForRow: row inSection: section];
Now get the slider that is in the cell in that section,row of the table
UITableViewCell *sliderCell = [tableView cellForRowAtIndexPath: indexPath];
UISlider *slider = [[sliderCell.contentView subviews] objectAtIndex: 0];
This assumes the slider is always the only view in the contents of the cell.
This method is a bit longer than some of the other suggestions above, but it keeps you from having to cache references off to the side.
For each cell, set a delegate link back to the table view controller, and also some kind of row reference ID - then wire the switch to a cell IBAction method, that calls back to the delegate with the reference ID for that cell.
What you can do is either have an Array of arrays or a dictionary, key it by the section number (or in case of the array they will be in order of the section numbers), now to retreive a switch all you do assuming you know the section and the row number
UISwitch *switch=[[switchArray objectAtIndex:section] objectAtIndex:row];
or if you have a dictionary
UISwitch *switch=[[switchDictionary objectForKey:section] objectAtIndex:row];

How to find the row index of a UIButton is a TableCell? (iPhone)

In my app, I have a table displaying a cell in each row.
In Interface Builder, I dragged a button onto the cell, styled it as a Dark Info button, and connected it to a IBAction.
That is working fine.
Only, I want the button to behave differently, depending on the row of the table where the cell of the button is.
How would I get that row index?
I realize that I might display a lack of basic understanding of the object hierarchy, but I hope you guys will forgive me
Thanks
Sjakelien
It's definitely not easy to do if you don't have some data set up first. If you can, have an NSDictionary where the buttons are the keys and the values are the index paths, that you update whenever you return a cell from -tableView:cellForRowAtIndexPath:. Something like this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
...
[indexDict setValue:indexPath forKey:theButton];
return cell;
}
- (void) buttonPressed:(UIButton *)button {
NSIndexPath *indexPath = [indexDict valueForKey:button];
...
}
You can maintain tags. When you drag and drop the button, check the interface build and you will see "tag" property for these buttons. Assign different values for each of your button ( I assume you have different buttons for different rows, this solution will not work if you have same cell identifier for different rows ). And when you receive an event check for tag value.
I had similar problem with my work and i was maintaing NSArray for each button tag created.
In your tableView delegate and datasource methods (check the docs!) you have several methods, the best one for this is
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
Drop this method in your implementation and say something like
switch (indexPath.row) {
case 0:
//set variable or do method based on row 0 (first row)
break;
case 1:
//set variable or do method based on row 1 (second row)
break;
case 2:
//set variable or do method based on row 2 (third row)
break;
}//and so on
}
another way is to change the base class of your UIButton in your view,
then using this other class wich basically extends an UIButton with an added NSInteger row #property (remember to #synthesize it).
You'll then set this property during the cell setup, and you can retrieve this property within the message method