I put a tableview in my storyboard, and filled the cell with a button with "0" as the tag. I populated the tableview using an array. Let's just say I have 20 elements in my array, that makes it 20 cells on the tableview. This is the code I'm using to give tags to the button on each cell.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
if (tableView == _tblCart) {
static NSString *cellIdentifier = #"CartCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
cell.selectionStyle = UITableViewCellSelectionStyleGray;
UIButton *btn = (UIButton *)[cell viewWithTag:0];
[btn setTag:indexPath.row];
[btn addTarget:self action:#selector(logTag:) forControlEvents:UIControlEventTouchUpInside];
return cell;
}
return nil;
}
I thought my code will work just fine, but actually there is something wrong. On cell 0-5, the button tag is correct. On the following cell the tag will reset again to 0. What am I doing wrong?
There is also another logic problem here, you are reusing table cells, some of which you've changed the tag of the button to something other than 0. So if you get a reused tablecell, there will come a time that the UIButton won't have a tag of zero and therefore you won't be able to change it correctly.
Don't set 0 as a tag identifier to views.
All UIViews have a tag by default 0.
So
[cell viewWithTag:0];
probably will return the contentView of the cell.
Both Flexicoder and l0gg3r are correct. Also, relying on button's tag as row identifier is a bit of a clunky workaround in my opinion. Consider this approach:
- (void)logTag:(UIButton *)sender {
NSIndexPath *indexPath = [_tblCart indexPathForRowAtPoint:[_tblCart convertPoint:sender.center fromView:sender]];
// do your stuff knowing which indexPath the button belongs to
}
Related
I used this code in cellForRowAtIndexPath method. When I click on button or scroll table this button is shown two times. Why is this button shown 2 times please help me?
UIButton *trashbtn=[[UIButton alloc]initWithFrame:CGRectMake(cell.frame.size.width-20, cell.frame.size.height-30, 20, 20)];
[trashbtn setImage:[UIImage imageNamed:#"editor_trash"] forState:UIControlStateNormal];
[trashbtn addTarget:self action:#selector(DeleteMyAssociate:) forControlEvents:UIControlEventTouchUpInside];
[trashbtn setTag:indexPath.row];
[cell addSubview:trashbtn];
You should firstly add a UITableViewCell within your table in IB. Then give an identifier e.g. "MyCellIdentifier" to that cell. Still on IB, add your outlets, to that cell e.g. the button, the textFields... You can initially set the button to be invisible. Then in the method cellForRowAtIndexPath, you do:
- (UITableViewCell*)tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"MyCellIdentifier"];
if(cell){
myButton.hidden = NO; //
myTextField.text = #"BlaBlaBla";
}
}
Hey I can give you a suggestion that use auto layout and storyboard to add button rather than adding it programmatically. That is more better and cleaner approach.
The problem you are facing is that cells are reused in tableviews, So the first time you create a button and add it to the cell, it appears once. But if it is dequeued, it already has the button, so when you add another one you end up with multiple buttons.
There are two ways to correct this.
Firstly, remove the button in the cell's prepareForReuse method, which is called just after the cell is reused.
Secondly, avoid creating custom views in the cellForTableView... method. Use a custom cell that already has the button. Now, you may ask, how do I hook up the action for the button if I do it this way? You can either provide a delegate method for your cell that calls back to your view controller, or you can pass your cell a block to perform when the button is clicked.
as far as i can get u already have a custom cell.. add a button to existing cell and create an IbOutlet for the same in customCell.h file
then in cellForRowAtIndexPath method access the button using its IBOutlet
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
customCell *cell =(customCell*) [tableView dequeueReusableCellWithIdentifier:#"customCell" forIndexPath:indexPath];
[cell.btn setTitle:#"blabla" forState:UIControlStateNormal];
[cell.btn setTag:indexPath.row];
[cell.btn addTarget:self action:#selector(method:) forControlEvents:UIControlEventTouchUpInside];
if(condition)
{
cell.btn.hidden=YES;
}
else
{
cell.btn.hidden = NO;
}
return cell;
}
Do not forget to register for the customCell class in your ViewController class
[tableName registerNib:[UINib nibWithNibName:#"customCell" bundle:nil] forCellReuseIdentifier:#"customCell"];
you can hide or unhide the button as per your requirements as well as add different actions to the button in each class.
Happy Coding..!!
Do vote it if my code was helpful for you.. ;)
Having issues updating uibutton in specific uitableviewcell. When I change the button color it updates every button in uitableview. Attached my code below:
PFObject *post = [self.objectArray objectAtIndex:indexPath.section];
cell.likeBtn.layer.cornerRadius = 5.0f;
//To access button methods
[cell.likeBtn setTag:indexPath.section];
NSMutableDictionary *attributes = [NSMutableDictionary
dictionaryWithDictionary:[[TMMemoryCache sharedCache] objectForKey:post.objectId]];
BOOL likedByCurrentUser = [[attributes objectForKey:#"likedByCurrentUser"] boolValue];
if (likedByCurrentUser) {
cell.likeBtn.backgroundColor = [UIColor flatRedColor];
[cell.likeBtn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
[cell.likeBtn setTitle:#"Liked" forState:UIControlStateNormal];
}else{
//NSLog(#"Did not like %#", post.objectId);
cell.likeBtn.backgroundColor = [UIColor flatBlueColor];
[cell.likeBtn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
[cell.likeBtn setTitle:#"Like" forState:UIControlStateNormal];
}
is their a better way to update uibutton in just one specific cell?
There are two ways to do what you want, depending on how the updated information comes in:
When configuring the cell in cellForRowAtIndexPath:.
Let the cell manage its own contents.
More often than not, #1 is the way to go. When new information comes into your UITableViewController, do 1 of the following:
Call reloadData to force a refresh of the entire table.
Figure out which cells need to be updated and call reloadRowsAtIndexPaths:withRowAnimation: passing in the correct index path values.
In either of those scenarios, you'll set up the button correctly somewhere in your tableView:cellForRowAtIndexPath: method. Generally, I create my UITableViewCell subclasses to accept an object and configure themselves with the data in that object. That way, there's less code in my UITableViewController that deals with cell configuration.
A typical implementation of tableView:cellForRowAtIndexPath: looks like this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
Cell *cell = [tableView dequeueReusableCellWithIdentifier:#"item" forIndexPath:indexPath];
[cell configureForItem:self.dataSource[indexPath.row]];
return cell;
}
Each of your UITableViewCell instances will have their own button that will be configured by the data passed by the object in self.dataSource[indexPath.row]. That way, as cells are recycled, the button isn't just passed around, but a new one is recycled and configured each time a new cell is needed.
At a guess, without seeing more of your code I would say your issue is with your use of indexPath.section instead of indexPath.row
If you have multiple buttons per section then they will all be covered by this code.
I want to declare the UIButton only once that will show in each table row and then will use button.tag to make button work accordingly.
You will need to have the button created for each table row. You can use prototype cell if you are using Storyboard.
The reason it will not work with single button as a view can be added to one super view at a time. If you add button to second row cell it will be removed from the first row.
Create a Custom cell and design you cell as per your requirement then after in Table View Data Source Method you can set the tag and then after you can create your button method and as per tag use Switch case to do different different action.
Happy Coding!!!
This is a UITableView with UiButton in each row LINK
Code here:-
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *simpleIdentifier=#"tableItems";
UITableViewCell *cell=[tableView dequeueReusableCellWithIdentifier:simpleIdentifier];
if (cell==Nil)
{
cell=[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleIdentifier];
}
UIButton *tableButton=[UIButton buttonWithType:UIButtonTypeRoundedRect];
tableButton.frame=CGRectMake(140, 5, 60,30);
NSLog(#"%f---%f",cell.center.x,cell.center.y);
[cell addSubview:tableButton];
cell.textLabel.text=[TableArray objectAtIndex:indexPath.row];
cell.textLabel.textColor=[UIColor blueColor];
cell.textLabel.hidden=YES;
cell.accessoryType=UITableViewCellAccessoryDisclosureIndicator;
return cell;
}
I have a NSTimer that calls this method every fourth second:
- (void)timerDecrement
{
timerCount = timerCount-1;
[OtherViewControllerAccess updateTimeLeftLabel];
}
In the updateTimeLeftLabel in the other class:
- (void)updateTimeLeftLabel
{
int timeLeft = OtherClassAccess.timerCount;
UILabel *timeLeftLabel = [[UILabel alloc] initWithFrame:CGRectMake(200, 10, 120, 20)];
timeLeftLabel.text = [NSString stringWithFormat:#"Tid kvar: %ih", timeLeft];
[cell addSubview:timeLeftLabel];
}
Basically I want my app to update a label in a cell in the tableview with the current time left, but the above method doesn't do anything to the call. So my question is, how can I add this subview to the cell outside the cellForRowAtIndexPath:delegate method, and then make it update that label every time the method is called.
So my question is, how can I add this subview to the cell outside the
cellForRowAtIndexPath:delegate method, and then make it update that
label every time the method is called.
The answer is, don't add subviews to a table view cell outside of cellForRowAtIndexPath. The cells belong to the table view, and you absolutely, categorically, should NOT try to modify them. That's the table view's job.
Just as a small example of what's wrong with your code, you would be adding an ever-increasing number of label views to your table view cell, one every 1/4 second. That's bad.
Second point: Which cell is "cell"? A table view manages a whole table of cells. If the user scrolls, some cells are scrolled off-screen and replaced with different cells.
Instead, you should figure out which indexPath contains the cell with your data in it, change the data in your model, and tell your table view to update the cell at the appropriate indexPath. That will cause it to redraw with updated contents.
Here is how I did something similar. I created a custom UITableViewCell class that has a timestamp UILabel:
#property (nonatomic) UILabel *labelTimestamp;
In that cell's layoutSubviews, I update the label size based on its title.
- (void)layoutSubviews {
[super layoutSubviews];
[self.labelTimestamp sizeToFit];
...
}
I then have an NSTimer firing every minute in my UIViewController that update that label in every visible cell (you could adapt to update only one cell with a specific indexPath).
- (void)timerDidFire
{
NSArray *visibleCells = [self.tableView visibleCells];
for (GroupViewCell *cell in visibleCells) {
NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
[cell.labelTimestamp setText:[self.groupController statusUpdateDateAtIndexPath:indexPath]];
[cell setNeedsLayout];
}
}
I would keep the set up of the cell's centralized in cellForRowAtIndexPath: method. You can keep using your NSTimer to call reloadRowsAtIndexPaths:withRowAnimation: with the indexes of the cell/cells you want to update and therefore cellForRowAtIndexPath: will be called again.
Why don't you put that value (which you want to display in the cell) in a variable and assign a UILabel that value. In your updateTimeLeftLabel just call [self.tableView reloadData].
You can reload whole table or some specific rows but that needs connection of datasource with your views . You have to change your dataset first and then you have to call
[self.tableview reloadData];
another method is that, after changing dataset call
[self.tableview reloadRowsAtIndexPaths:indexPath withAnimation:animation];
2nd method requires indexPath i.e. you know that which cell you need to edit .
My problem was same . In my project there were 2 TextFields and 1 label in each cell . Now depending on the values of 2 textfields, I have to show their multiplication in UILabel. and this is my code.
-(void)textFieldDidChange:(id)sender{
UITextField *_sender = (UITextField *)sender;
int tag = _sender.tag;
int row = tag / 3;
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:0];
((UILabel *)[[self.tableView cellForRowAtIndexPath:indexPath] viewWithTag:row * 3 + 2]).text = #"hello";
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
NSLog(#"here");
static NSString *CellIdentifier = #"procedureDetailsCell";
UITableViewCell *cell = (procedureCell *)[self.tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if ( cell == nil ) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
[((procedureCell *)cell).Quantity setTag:indexPath.row + 0];
[((procedureCell *)cell).Quantity addTarget:self action:#selector(textFieldDidChange:) forControlEvents:UIControlEventEditingChanged];
[((procedureCell *)cell).Cost setTag:indexPath.row + 1];
[((procedureCell *)cell).Cost addTarget:self action:#selector(textFieldDidChange:) forControlEvents:UIControlEventEditingChanged];
[((procedureCell *)cell).total setTag:indexPath.row + 2];
return cell;
}
Logic is simple I have used Custom TableViewCell which contains 2 textfields and 1 label. when one of the two textfield's value is changed we are calling "textFieldDidChange" method which finds indexpath and then find UITableViewcell and then updates its lastview's text value, that is our UILabel. we have to give unique tag to each of our views .
I have simply dragged a custom style button to a cell in storyboards. The problem is that when I press the button, it clicks the cell and not the button.
What could cause this?
Do I need to increase the click area? If so how?
Do I need to bring the button to the front? If so how?
Thanks!
My cell for row at index path looks like:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
MainUserViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
[cell initWithStyle:nil reuseIdentifier:nil];
[cell.descrlptionLabel sizeToFit];
[cell.userNameButton addTarget:self action:#selector(userNameClicked:) forControlEvents:UIControlEventTouchDown];
return cell;
}
No need for the initWithStyle line in your method. You should eliminate it.
[cell initWithStyle:nil reuseIdentifier:nil];
The cell is dequeued and should already be ready and initialized. You are working with storyboard, obviously, so the dequeue method is guaranteed to return a valid cell, auto-creating it if necessary.
This is most likely messing things up.
Some more things to check: Make sure the button is topmost in your story board file (i.e. the bottom item in the list on the left). Your resizing of the label could cover it otherwise. Also, make sure you did not accidentally set userInteractionEnabled to NO.