I have UITableView which covers whole screen (480px).
Each cell is of height 300px.
I have total 5 rows.
Below is the code what I have used.
-(UITableViewCell *) tableView:(UITableView *) tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"MainCell"];
if (cell==nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:#"MainCell"];
}
UIButton *myButton = (UIButton *)[cell viewWithTag:999999999];
int i = indexPath.row+1;
myButton.tag = i;
myButton.titleLabel.text = [NSString stringWithFormat:#"B - %d", i];
[myButton setTitle:[NSString stringWithFormat:#"B - %d", i] forState:UIControlStateNormal];
NSLog(#"tag set is %d & text for button is =====B-%d====", i,i);
[myButton addTarget:self action:#selector(btnSelected:) forControlEvents:UIControlEventTouchUpInside];
return cell;
}
- (int) tableView:(UITableView *) tableView numberOfRowsInSection:(NSInteger)section {
return 5;
}
-(IBAction)btnSelected:(id)sender {
UIButton *button = (UIButton *)sender;
NSLog(#"my tag after click is ===%d", [button tag]);
}
Now when I run this code, I was expecting each cell will have B - 1 to B - 5 written. However after B - 3, all I see is B - 1 and B- 2.
NSLog says as below.
2013-07-28 23:31:34.281 NewTest[1783:11303] tag set is 1 & text for button is =====B-1====
2013-07-28 23:31:34.284 NewTest[1783:11303] tag set is 2 & text for button is =====B-2====
Now when I scroll fully down, I get NSLog as below.
2013-07-28 23:31:34.281 NewTest[1783:11303] tag set is 1 & text for button is =====B-1====
2013-07-28 23:31:34.284 NewTest[1783:11303] tag set is 2 & text for button is =====B-2====
2013-07-28 23:32:03.643 NewTest[1783:11303] tag set is 3 & text for button is =====B-3====
2013-07-28 23:32:03.719 NewTest[1783:11303] tag set is 4 & text for button is =====B-4====
2013-07-28 23:32:03.835 NewTest[1783:11303] tag set is 5 & text for button is =====B-5====
Now when tag and text are set properly why I see last two buttons as B-1 and B-2 instead of B-4 and B-5.
Any idea how to solve this problem?
Screenshot 1
Screenshot 2
Screenshot 3
Any idea how to solve this problem so that I have B-1 to B-5 written?
Note : If I decrease the height of the cell to 100, I see all button text as B-1 to B-5.
This question is related to my old question, but this is simpler version.
Sample Project
What I did is, not used tag and using accessibilityValue, I am fetching the button clicked id.
As you scroll down, the table view will reuse the cells that are no longer visible as it needs to display additional cells. The first time this happens is for cell "B-3". Because the table view is reusing a cell, the tag for the button in the cell was already previously set by your code to some number (probably 1). Thus, viewWithTag:999999999 will return nil. Your NSLog statements will make it look like things are working correctly, but actually you're trying to set a title on a nil button, thus the button in the reused cell does not get updated with the correct title.
For a solution: you could create a custom subclass of UITableViewCell with a property:
#property (nonatomic, weak) IBOutlet UIButton *button;
And in the storyboard or xib wire this up to your button. Or if necessary you could programmatically do this in the cell's awakeFromNib or init method. Now you can directly reference cell.button instead of using [cell viewWithTag:9999999].
Related
How to get the Dynamic tableview cell data.
I have a table view like this. All the cells are creating dynamically.
I want to get all the text (flavor and %)data when submit button is pressed.
The problem was I creating text in dynamically. So I cannot individually identify the text box. How could get the data from dynamic text box?
Set tag to your uitextfield in your custom cell
For eg.
cell.lblFlavour.tag = indexPath.row;
cell.lblPercent.tag = indexPath.row;
Now, access your uitextfield on the basis of your indexPath
Use tag property to identify each UI component uniquely. Assign the same tag property where you are creating textFields and button to each row then after change tag value for next row and get them using code.
To set tag:
self.yourTextfield.tag = 100;
self.yourbutton.tag = 100;
// set action method to uibutton
[cell.yourButton addTarget:self action:#selector(buttonPressed:) forControlEvents:UIControlEventTouchUpInside];
To get the UI components using tag property:
-(IBAction)buttonPressed:(id)sender{
// here get all ui component using tag
UIButton *button = (UIButton *)sender;
NSLog(#"%d", [button tag]);
}
or you can use method didSelectRowAtIndexPath to get values see code:
- (void) tableView: (UITableView *) tableView didSelectRowAtIndexPath: (NSIndexPath *) indexPath {
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
NSLog(#"getting index path %#",indexPath);
NSLog(#"getting index path %#",cell.yourTextField.text);
}
You can set tag to your textfield and by that tag you can access them.
For example,
UITextField *textField = [[UITextField alloc]initWithFrame:CGRectMake(0, 0, 200, 30)];
textField.tag = 100; //set tag
UITextField *textField1 = (UITextField*)[self.view viewWithTag:100]; //retreive by tag
So, like this you can manage your dynamically made textfields.
Update :
then set two tag 100 and 101 respectively. Now you have to got cell first and from that cell you can got your textfields so on button's click first got all cell, then you can got textfield from that cell like :
UITextField *textField = (UITextField*)[cell viewWithTag:100];
So, you have to got cell first in your button's click.
The main idea of UITableView is store data into data source, not into cell. When you tap "+" button on the navigation bar, you should add new item into array and then call [tableView reloadData]. So a UITableView just represent data from array. And when submit button will pressed you just enumerate an array of data source and get all the text from each row.
When you tap text into text field inside the cell, you could use delegates or blocks to store data into array.
try below code :
When you click on submit button
for (int i=0; i < [[tableView visibleCells] count]; i++)
{
//yourArrayname is the number of rows in the UITableView /Or UITableView visible cells
NSIndexPath *indexPath = [NSIndexPath indexPathForRow: i inSection: 0];
CustomTableViewCell *cell = [tblExperience cellForRowAtIndexPath:indexPath];
for (UIView *view in cell.contentView.subviews){
if ([view isKindOfClass:[UITextField class]]){
UITextField* CurrentTextField = (UITextField *)view;
NSLog(#"val %d %#",i,CurrentTextField.text);
[finalArray addObject:CurrentTextField.text];
}
}
}
Assign dynamic tags to your textfields, implement textfield delegates and save the data in an array whenever textfield ends editing. Something like this
-(void)textFieldDidEndEditing:(UITextField *)textField {
[_array insertObject:textField.text atIndex:textField.tag];
}
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
}
I'm trying to create an friend request function. Where all the friend request would show up in a table and a player will get to click accept or decline. What I'm trying to do is create an accept button beside this UITableView that contains all the player's friend requests.
Here's my code.
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *notificationCell = [tableView dequeuREusableCellWithIdentifier#"notificationCell" for IndexPath:indexPath];
NSArray *friendRequests = [self fetchAllFriendRequestsInArray];
NSManagedObject *friendRequestingRelationship = [friendRequests objectAtIndex:indexPath.row];
notificationCell.textLabel.text = [friendRequestingRelationship valueForKey:#"name"];
UIButton *acceptButton = [UiButton buttonWithType:UIButtonTypeSystem];
[acceptButton.frame = CGRectMake(notificationCell.frame.origin.x + 150, notificationcell.frame.origin.y -20, 80, 40);
[acceptButton setTitle:#"Accept" forState:UIControlStateNormal];
acceptButton.backgroundColor = [UIColor clearColor];
[acceptButton addTarget:self action:#selector(acceptButtonPressed) forControlEvents:UIControlEventTouchUpInside];
[notificationCell.contentView addSubview:acceptButton];
return notificationCell;
}
Only the first notificationCell showed the friendrequester's name and Accept Button. Other notificationCells only showed other friendrequesters' names without the button. May I know what is wrong with my code such that I can allow the button to be shown on every single cell?
Thank you in advance!
The buttons are there, but they are clipped from the view. This line is the culprit:
acceptButton.frame = CGRectMake(notificationCell.frame.origin.x + 150, notificationcell.frame.origin.y -20, 80, 40);
You shouldn't add the origin of notificationCell to the button, because subview positions are relative to positions of their superviews.
This should give you the right look, but your code has other potential problems.
The line where you fetch all friend requests is probably too slow to be executed for each cell in the view. Make sure that the results are cached
Table view cells are recycled. When one of such recycled cells makes it to your code, it looks like your code adds a second button on top of the first one
Similarly, if a recycled cell with a button is returned for the cell that does not need a button, the old button would remain visible.
You may be better off using a prototype cell that already has a button on it. Instead of adding and removing that button, you could make the existing one visible or invisible, depending on the context.
This is a bad approach to solving this problem to begin with. You should go to the storyboard, and drop a button in a prototype cell. Then select that button, go to the attributes inspector, and set the "tag" to a number of your choice (for this example we will say 1). You can then get the button for each cell like so after grabbing the cell:
UIButton * acceptButton = (UIButton *)[cell viewWithTag: 1];
[acceptButton addTarget:self action:#selector(acceptButtonPressed) forControlEvents:UIControlEventTouchUpInside];
This is cleaner and will give you the results you want.
In my application I am creating UITextField within UITableViewCell so that user can fill some information. This is my code:
if (![cell.contentView viewWithTag:10])
{
UITextField *textField=[[UITextField alloc]initWithFrame:CGRectMake(0, 0, 200, 30)];
[textField setTag:10];
[textField setBorderStyle:UITextBorderStyleRoundedRect];
[textField setBackgroundColor:[UIColor redColor]];
[cell.contentView addSubview:textField];
}
I am doing this so that I create that UITextField only once for each cell so that the textFields don't overlap... but a problem happened for example when the user writes in the textfields:
row 0 -> 0
row 1 -> 1
row 2 -> 2
row 3 -> 3
row 4 -> 4
And so on, if you have number of rows more than 10 and you started to scroll I can notice that the cells exchange there indices randomly so i can get something like:
row 0 -> 3
row 1 -> 1
row 2 -> 2
row 3 -> 0
row 4 -> 4
in the UITextField.text
how to make something or a trick like fixed position for each cell?
This can look as a 'bad' decision, but I would strongly recommend you Not to use UITableView with UITextFields unless you have like 40 textfields, because of the following reasons:
It lags the way you described, because NSIndexPath is calculated on
the go and the text that user filled in the textfields would jump
around the table when you scroll.
You will have a mess when implementing logics to get out text from
the textfields.
You will have to implement a big piece of code to implement "scroll
table up and down" animation to let user navigate through textfields,
i.e in textFieldDidBeginEditing
If you still want to use tableview, i would recommend you to set some key to each of your UITextFields, and create a datasource (like NSDictionary) that binds this key to a certain text value that you can get from it any time.
Then you will probably have an ugly piece of code like this:
- (IBAction)textFieldValueChanged:(UITextField *)sender
{
MyTableCell *cell = (MyTableCell*)sender.superview.superview;
[self.formDataSource setObject:cell.formTextField.text forKey:cell.contentKey];
}
Then when you need you can enumerate through formDataSource allValues property.
Here's an abbreviated sample of how I do this in one of my view controllers:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell
= [tableView dequeueReusableCellWithIdentifier:AttributeCellIdentifier];
NSString *propertyName = #"...";
NSString *className = #"...";
UILabel *label = (UILabel*)[cell.contentView viewWithTag:1];
UITextField* textField = (UITextField*)[cell.contentView viewWithTag:2];
textField.text = [self stringForAttribute:propertyName];
label.text = propertyName;
return cell;
}
tableView:cellForRowAtIndexPath: is part of the UITableViewDataSource protocol. It gets called whenever the table view needs to scroll new cells into view. Since it is recycling cells that have scrolled out of view automatically, you need to reset the state of the cell in this method based on the values in your model.
You may also find it useful to know you can configure your custom cell in Interface Builder, in your Storyboard. Just be sure to set an appropriate reuse identifier so that your controller will always be able to supply the right type of cell.
I have a UITableView with custom Cell (subclass of UITableViewCell). Each cell view has two buttons btn1 and btn2. I set my ViewController to handle the TouchUp Inside of those btn1s.
How do I know in which cell button clicked?
I'd put a tag property on it. Say the left one has a tag of 1, and the right has a tag of 2.
All UIViews have a tag property, which is just an integer value.
#property (nonatomic) NSInteger tag;
Then when you have received a button tap, you can ask what the button's tag is - 1 or 2, and you'll know which button it was.
If you want to know which index path that button belonged to, you can grab the button's superview, which should be the cell, or the button's superview's superview, if you placed it on the cell's content view (as you generally should):
UITableViewCell *cell = (UITableViewCell *)[button superview];
NSIndexPath *pathForSelectedButton = [tableView indexPathForCell:cell];
Note that the above code assumes the button was placed directly on the cell.
UIButton has a tag property. You can set a unique value to each of your button, such as indexPath.row*2 and indexPath.row*2 + 1 if there is only one section.
And then you can add target method for each button.
[button addTarget:self action:#selector(buttonPressed:) forControlEvents:UIControlEventTouchUpInside]
Lastly, implement buttonPressed method like
- (void)buttonPressed:(UIButton *)sender {
NSInteger tag = sender.tag;
// your code here.
}