I am wondering whether someone has had similar problem that I am experimented lately.
Let me describe you a little bit further about my issue. I have got a UITableViewController where I have designed a set of custom UITableViewCell in the IB. Each UITableViewCell has got different elements like a UIButton, UITextField and UILabel, etc. Obviously, each UITableViewCell has a different identifier.
Once defined all my UITableViewCells, next step is to instantiate the cell on tableView:cellForRowAtIndexPath:. What I do in this method is depending on section and row, I instantiate the different UITableViewCells by dequeueReusableCellWithIdentifier:forIndexPath:.
Everything seems to work perfectly and correctly created on the table, but the problem arrives when I scroll down. At the very top, I have got a UITableViewCell with a UIButton that I have specified with a concrete action to perform when it is clicked. Scrolling down, there are a couple of UITableViewCells with the same format (an UIButton inside with different actions specified).
The problem is, when a click the first button from the bottom side, this button performs the first action that I have defined on the very top UIButton and its own action.
It seems that when uitableviewdelegate creates new cells or reuse them, it nests functionalities from other indexPath instead of the specified indexPath....
Hope that I have explained myself properly.
Thank you in advance.
[EDIT]
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *cell = [[UITableViewCell alloc] init];
NSLog(#"indexpath value is: %#", indexPath);
if (indexPath.section == 0)
cell = [self buildSection_0:tableView getIndexPath:indexPath];
else if (indexPath.section == 1)
cell = [self buildSection_1:tableView getIndexPath:indexPath];
else if (indexPath.section == 2)
cell = [self buildSection_2:tableView getIndexPath:indexPath];
else if (indexPath.section == 3)
cell = [self buildSection_3:tableView getIndexPath:indexPath];
else if (indexPath.section == 4)
cell = [self buildSection_4:tableView getIndexPath:indexPath];
return cell;
}
-(UITableViewCell *)buildSection_0:(UITableView *)tableView getIndexPath:(NSIndexPath *)indexPath{
NSString *identifier;
UITableViewCell *cell;
if (indexPath.row == 0){
identifier = #"headerCell";
cell = [tableView dequeueReusableCellWithIdentifier:identifier forIndexPath:indexPath];
}
else if (indexPath.row == 1){
identifier = #"buttonCell";
cell = [tableView dequeueReusableCellWithIdentifier:identifier forIndexPath:indexPath];
UIButton *button = (UIButton *)[cell viewWithTag:3001];
[button setTitle:#"Scan" forState:UIControlStateNormal];
[button addTarget:self action:#selector(scan) forControlEvents:UIControlEventTouchUpInside];
}
return cell;
}
-(UITableViewCell *)buildSection_3:(UITableView *)tableView getIndexPath:(NSIndexPath *)indexPath{
NSString *identifier;
UITableViewCell *cell;
if (indexPath.row == [[job jobLocations] count]) { // Adding button insert more Locations
identifier = #"buttonCell";
cell = [tableView dequeueReusableCellWithIdentifier:identifier forIndexPath:indexPath];
UIButton *button = (UIButton *)[cell viewWithTag:3001];
[button setTitle:#"Add Location" forState:UIControlStateNormal];
[button addTarget:self action:#selector(newLocation) forControlEvents:UIControlEventTouchUpInside];
}
else{ // Showing different Locations
identifier = #"locationCell";
cell = [tableView dequeueReusableCellWithIdentifier:identifier forIndexPath:indexPath];
UILabel *label = (UILabel *)[cell viewWithTag:3007];
UITextField *textField = (UITextField *)[cell viewWithTag:3008];
[label setText:[NSString stringWithFormat:#"Location #%ld", (long)indexPath.row + 1]];
[textField setText:[[[job jobLocations] objectAtIndex:indexPath.row] locationType]];
}
return cell;
}
dequeueReusableCellWithIdentifier:forIndexPath will reuse cells that are no longer visible, you may want to implement prepareForReuse on your cell, or reset it after you dequeue it.
[EDIT]
Seeing you added your code, you should remove previous target/actions from the button before adding the new ones, as the old one will still be there if a cell is being reused:
[button removeTarget:self action:NULL forControlEvents:UIControlEventTouchUpInside];
[button addTarget:...
Add a tag to the button that corresponds to the indexPath.row. Then in your button action method, you can determine which button was clicked by looking at the (UIButton *)sender's tag.
Related
i have a tableview in Main Controller which has its custom cell that contains 3-4 button.. see screenshot
when button getpressed it populate a small tableview , when i select row of new tableview it not works a i expected see screenshot
Now issue is its setting selected value of new tableview to oldtableview same index
here's code :
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *MyIdentifier = #"FollowUp";
UITableViewCell *cell ;//= [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:MyIdentifier] ;
if (tableView == tblScribedBy) {
[cell.textLabel setText:[arrScribedBy objectAtIndex:indexPath.row]];
[cell.textLabel sizeToFit];
}
if (tableView == tblDropDown) {
if ([btnCLicked isEqualToString:#"Cell"]) {
[cell.textLabel setText:[totalRows objectAtIndex:indexPath.row]];
[cell.textLabel sizeToFit];
}
else if ([btnCLicked isEqualToString:#"Drop"]){
[cell.textLabel setText:[arrFUDrop objectAtIndex:indexPath.row]];
}
}
if (tableView == tblView) {
FUCellView * cell = [tableView dequeueReusableCellWithIdentifier:#"myCell"];
if (!cell)
{
[tblView registerNib:[UINib nibWithNibName:#"FUCellView" bundle:nil] forCellReuseIdentifier:#"myCell"];
cell = [tblView dequeueReusableCellWithIdentifier:#"myCell"];
}
[cell.btntfFUCell addTarget:self action:#selector(actionTfCellFU:) forControlEvents:UIControlEventTouchUpInside];
[cell.btntfFUCell setTag:indexPath.row];
[cell.btnDropFU addTarget:self action:#selector(actionDropFU:) forControlEvents:UIControlEventTouchUpInside];
[cell.btnDropFU setTag:indexPath.row];
return cell;
}
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
FUCellView *cell = (FUCellView *)[tblView cellForRowAtIndexPath:indexPath];
if ( tableView == tblScribedBy) {
[self.btnScribedBy setTitle:[NSString stringWithFormat:#" %#",[arrScribedBy objectAtIndex:indexPath.row]] forState:UIControlStateNormal ];
[tblScribedBy setHidden:TRUE];
}
else if (tableView == tblDropDown) {
if ([btnCLicked isEqualToString:#"Cell"]) {
[cell.tfFUCell setText:[totalRows objectAtIndex:indexPath.row]];
}
else if ([btnCLicked isEqualToString:#"Drop"]){
[cell.btnDropFU setTitle:[arrFUDrop objectAtIndex:indexPath.row] forState:UIControlStateNormal];
}
[tblDropDown setHidden:YES];
}
}
//----- Action for Buttons
-(void)actionTfCellFU:(UIButton *)sender
{
btnCLicked = #"Cell";
[tblDropDown setHidden:FALSE];
[tblDropDown reloadData];
}
-(void)actionDropFU:(UIButton *)sender
{
btnCLicked = #"Drop";
[tblDropDown setHidden:FALSE];
[tblDropDown reloadData];
}
FUCellView *cell = (FUCellView *)[tblView cellForRowAtIndexPath:indexPath];
This line is being used to get the cell to update, but it's using the index path selected on the second table view to get the target cell on the first table view. Hence you get an index error and the wrong cell is updated.
When actionDropFU: is called you need to determine a way of storing, as an instance variable, the index path of the cell which was selected. This is the index path on table 1 and is the one you want to use to update when setting the results of the selection from the second table later. In general you shouldn't be updating the cell directly, you should be updating your data model.
The best option for this is to not just add a target and selector to the buttons on your cells. Instead, subclass the cell and add properties to it so you can access them later when it is interacted with. The cell should be the target of the button clicks and it should call back to the view controller (as the cell delegate or target) when the buttons are tapped, passing the configured data (the index path).
I use storyboard and Auto Layout. I add UISwitch to my cell with tag 5. When I choose first UISwitch and scroll down I see that other UISwitch is also turned on and if I scroll up my first UISwitch is turned off. How to fix this?
My code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
UISwitch* switchView = (UISwitch *)[cell viewWithTag:5];
[switchView addTarget:self action:#selector(switchChanged:) forControlEvents:UIControlEventValueChanged];
return cell;
}
This is because UITableView reuse UITableViewCell so one cell can be use more than once in different indexPaths, in this situation its your responsibility to maintain the state of UITableViewCell subViews. Better place to do this is cellForRowAtIndexPath where you are returning cell add logic to make show/hide UISwitch or to select accurate state i.e. on or off, you can keep that flag in dataSource object and then you can check for that flag to make set right state for UISwitch
Try This:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"CellSetting";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
cell.textLabel.text = [self.settingsArray objectAtIndex:indexPath.row];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
if ([[self.settingsArray objectAtIndex:indexPath.row] isEqualToString:ROW_PRIVATE_BROWSING])
{
self.privateBrowsingSwitch =[[UISwitch alloc]initWithFrame:CGRectMake(cell.frame.size.width-65, 10, 30, 30)];
if (ApplicationDelegate.privateBrowsing)
{
[self.privateBrowsingSwitch setOn:YES animated:YES];
}
[self.privateBrowsingSwitch addTarget:self action:#selector(changeSwitch:) forControlEvents:UIControlEventValueChanged];
[cell addSubview:self.privateBrowsingSwitch];
cell.accessoryType = UITableViewCellAccessoryNone;
}
return cell;
}
Every time cellForRowAtIndexPath is called you have to replace the specific data that needs to be displayed for a cell at that position. This includes things like labels, images and your UISwitch.
This occurs because UITableViews use a small number of cells that are reused.
In cellForRowAtIndexPath add something like this:
switchView.on = [self isSwitchOnForCellAtIndexPath:indexPath]
Then write whatever logic is required to determine if the switch should be on or not.
I am trying to change the color of a button when pressed that is in a tableviewCell. However my code changes the color of every button in the table and not just the one in the cell I selected,
How would I go about just changing the color of the button I pressed.
Please see my code below,
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
UIButton *addNotesButton = (UIButton *)[cell viewWithTag:106];
[addNotesButton setTitleColor:[UIColor blueColor] forState:UIControlStateNormal];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Test";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
// Configure the cell...
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
UIButton *addNotesButton = (UIButton *)[cell viewWithTag:106];/
[addNotesButton addTarget:self action:#selector(addNotes :) forControlEvents:UIControlEventTouchUpInside];
}
The main issue might be in your cellForRowAtIndexPath: method. UITableView cells are re-used as they are displayed on the screen. dequeueReusableCellWithIdentifier: method returns a cell if it has been marked as ready for reuse. You must have seen this method of UITableView being used in cellForRowAtIndexPath: method. (See this link)
So in cellForRowAtIndexPath: you will have to configure each cell as it is being loaded or else it will display old values (since the cells are being reused).
You can either declare a property or a simple variable of type NSIndexPath.Let the variable be called _selectedIndexPath. Then in didSelectRowAtIndexPath: you can assign this property to the indexPath selected.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
NSArray *indexPaths = nil;
if (_selectedIndexPath) {
indexPaths = #[_selectedIndexPath, indexPath];
} else {
indexPaths = #[indexPath];
}
_selectedIndexPath = indexPath;
[tableView reloadRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationNone];
}
- (void)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Your Cell Identifier"];
UIButton *addNotesButton = (UIButton *)[cell viewWithTag:106];
if (indexPath.row == _selectedIndexPath.row) {
[addNotesButton setTitleColor:[UIColor blueColor] forState:UIControlStateNormal];
} else {
[addNotesButton setTitleColor:[UIColor clearColor] forState:UIControlStateNormal];
}
}
You don't need to change color manually in did select row at index path. Just set the color for UIControlStateSelected and on the action of button tap set the buttons selected property to YES. From your code i think this should work.
inside cell for row at index path method
[addNotesButton setTitleColor:[UIColor blueColor] forState:UIControlStateSelected];
and in button action method
-(IBAction)addNotes:(id)sender
{
UIButton *button = (UIButton*)sender;
buttons.selected = !button.isSelected;
}
I think this will work.
After trying everything and failed. I ended up having a hidden value in each row that would change when the button is pressed. So the code reads the value then configures the button for each row.
I created one prototype cell. Cell has one label and one button. I have given tag's for both.
Now i want to detect which button is clicked from 10 cells.
Previously we were differentiating that based on tag. But how to do this with prototype cell.
My code for cell creation is as follows:
- (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cellIdentifier"];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:#"cellIdentifier"];
}
UIButton *stopStartButton = (UIButton *)[cell viewWithTag:103];
UILabel *chargingLabel = (UILabel *)[cell viewWithTag:102];
}
-(IBAction)stopStartButtonClicked:(id)sender
{
NSLog(#"Button clicked");
}
You can use button.titleLabel.tag for differentiate your button and at action time you can compare with same tag.
second option is with your button action. you can append event so that is also provide you all information regarding your button.
For example you just set
stopStartButton.titleLabel.tag=1;
-(IBAction)stopStartButtonClicked:(id)sender
{
NSLog(#"Button clicked %d",sender.titleLabel.tag);
}
I think the best way is to find what cell does actually has button that was tapped. You can find it by calculating x origin point of button.
- (IBAction)stopStartButtonClicked:(id)sender
{
CGPoint pointInTable = [button convertPoint:button.bounds.origin toView:self.tableView];
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:pointInTable];
}
If you have indexPath, you can get cell:
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
I am currently building an iOS app that uses a UITableView with custom UITableViewCells that include a button. What I want to accomplish is having the UIButton's image change on touch up inside. That part is working fine, the issue is when you scroll, suddenly every few row buttons located in the UITableViewCell have this new image. Below is some of my code. Any help would be appreciated.
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
NSString *cellID = #"CellID";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID];
if(cell == nil){
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:cellID];
//YEP BUT
yepBut = [[UIButton alloc] initWithFrame:CGRectMake(23, 16, 64, 32.5)];
[yepBut addTarget:self action:#selector(yepPost:) forControlEvents:UIControlEventTouchUpInside];
[cell.contentView addSubview:yepBut];
}
[yepBut setImage:[UIImage imageNamed:#"yepBtn"] forState:UIControlStateNormal];
return cell;
}
-(void) yepPost:(id) sender{
//UIButton *clicked = (UIButton *) sender;
[sender setImage:[UIImage imageNamed:#"yepBtnACT"] forState:UIControlStateNormal];
}
Thanks!
You need to have a property that you set when the button is touched that you check in cellForRowAtIndexPath. So, in the button's method, have a property, say lastTouched (#property (strong,nonatomic) NSIndexPath *lastTouched, or NSInteger if you don't have sections), that you set to the indexPath (or indexPath.row) of the cell in which the touched button resided (gotten from the cell which you get by searching up through the superviews of the button until you find the cell, or by using tags on your buttons equal to the indexPath.row). After that, you have to reloadData or reloadRowsAtIndexPaths to make the change happen. In cellForRowAtIndexPath, you would set your images like this:
if ([self.lastTouched isEqual:indexPath]) {
[yepBut setImage:[UIImage imageNamed:#"yepBtnACT"] forState:UIControlStateNormal];
else{
[yepBut setImage:[UIImage imageNamed:#"yepBtn"] forState:UIControlStateNormal];
}
When you first initialize lastTouched, you want to set it to an indexPath that doesn't occur in your table, so nothing will have the yepButAct image until you touch one.
Subclassing UITableViewCell fixed my issue.