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.
Related
I got the following code to show a button on first UITableViewCellin case the NSArray with the search results is empty. Now, my numberOfRowsInSection has NSArray + 1, this way, my table will always have, at least, one cell.
This is in my cellForRowAtIndexPath:
if ([cell.contentView viewWithTag:1]) {
[[cell.contentView viewWithTag:1] removeFromSuperview];
}
if ((self.searchDisplayController.isActive && indexPath.row == [self.searchResults count]) || (!self.searchDisplayController.isActive && indexPath.row == [[self pegarObjetosUnicos] count])) {
// Create UIButton
UIButton *botaoProcurar = [UIButton buttonWithType:UIButtonTypeRoundedRect];
botaoProcurar.frame = CGRectMake(0, 0, 320, 50);
botaoProcurar.showsTouchWhenHighlighted = YES;
[botaoProcurar setTitle:#"Procurar no Banco de Dados" forState:UIControlStateNormal];
botaoProcurar.tag = 1;
[botaoProcurar addTarget:self action:#selector(botaoProcurar) forControlEvents:UIControlEventTouchUpInside];
[cell.contentView addSubview:botaoProcurar];
cell.textLabel.text = #"";
return cell;
}
The code first removes a subview, in case it finds one, and then create and add the button as subviewin case the search array is empty.
It works perfectly, I don't have any problems with this code, but since I need to do something similar now, I'm just wondering if this is the best approach. I'm just not comfortable with running this code for every cell my tableview has to render. I thought about adding the button to the NSArray when the database connection returned empty, but still, it wouldn't be a nice code.
Any suggestions for a more objective and clean code would be great. If not, a "Go for it! you are doing fine", will work as well. :)
You should place the button in the header or footer of the tableview. If the array has results, Remove the footer or header of the tableview. If the array has no results add the view to the footer or header. You should place this code in your function that decides if the array has data or not. This way the placement of the UIBUtton is not cell dependent, so now you won't have to run it for each and every cell you will have in the tableview.
I have a tableview with cells (not custom), and each cell has a button (among other things).
Depending on the cell, the button that is created is different. I have 3 different types of cell/button, Rate/View/Edit (I'm not sure that's at all relevant)
This button is only used for this:
[button addTarget:self action:#selector(rateEvent:) forControlEvents:UIControlEventTouchUpInside];
The selector is different for each button (rateEvent, viewEvent, editEvent).
Here is an example, they're all 3 fairly similar and start the segue sequence:
- (IBAction)viewEvent:(UIButton*)sender
{
[self performSegueWithIdentifier:#"fromHomeToDetails" sender:self];
}
My didSelectedRowAtIndexPath saves the details of a meeting in the selectedMeeting object.
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
selectedMeeting = [_nextMeetingsArray objectAtIndex:indexPath.row];
}
The segue sends selectedMeeting to the next controller.
My issue : The meeting that is sent depends on the selected cell. That means the user has to select a cell and THEN tap the button to get the correct details on the next page. What can i do to get the correct indexpath of a tapped button inside a cell?
I tried to add parameters to the viewEvent method, to make it look like -(void)viewEvent:(int)index:(UIButton*)sender; but it doesn't work in the #selector(viewEvent:).
The solutions i can imagine but not create are :
- Manage to get the indexpath of the cell of a tapped button and send it to my viewEvent method, somehow.
- Create a customCell and/or use an accessory, so i've read here and there.
- Force row selection when i tap the button, but that also requires to know the index of said row.
Both seem "too complicated" for something that (should?) be fairly simple. I'm pretty sure any experienced programmer will have an obvious easy answer that I haven't learned yet, and i'd rather ask for it than implement something heavier than it should be.
Also, if you have any comment/criticism on how i've done that, i'm all ears :)
Thanks for your time guys, as always, i'm very grateful.
EDIT :
cellForRow method :
Note : i removed everything that has to do with labeling. Because i have different buttons there are multiple if{} statements but they are of no influence in my opinion. Also, the cell is based on a storyboard cell with a tag of 228 ; after creation a new tag is set so it shouldn't influence. Also, i have a cap of 50 items in tableview (set from webservice) so there is no way the indexpath reaches >50.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *nextCell = [self.tbvNextMeetings dequeueReusableCellWithIdentifier:#"tbNextCell" forIndexPath:indexPath];
UIButton *bt= (UIButton*) [nextCell viewWithTag:228];
[bt setTitle:#"Edit" forState:UIControlStateNormal];
[bt addTarget:self action:#selector(editEvent:) forControlEvents:UIControlEventTouchUpInside];
[bt setTag:indexPath.row];
return nextCell;
Add tag to button as indexpath.row, in cellForRowAtIndexPath() method. Then in selector method get sender tag. And pass in
NSIndexPath *path = [NSIndexPath indexPathForRow:sender.tag inSection:yoursection];
UITableViewCell *cell = [tableView cellForRowAtIndexPath:path];
Edit : Setting the cell with a tag of 228 at first and then switching it to the value of indexpath made its creation fail.
I try to display a tableView with a button on each cell. The title of each button is a variable (an ID) in order to keep this value.
NSString *valuebutton = [NSString stringWithFormat:#"%#", [contactInfoDict objectForKey:#"idparse"]];
UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
button.frame = CGRectMake(cell.frame.origin.x, cell.frame.origin.y + 12, 300,35);
[button addTarget:self action:#selector(customActionPressed:) forControlEvents:UIControlEventTouchUpInside];
button.backgroundColor= [UIColor clearColor];
[button setTitle:valuebutton forState:UIControlStateNormal];
[cell.contentView addSubview:button];
return cell;
When I try to scroll the tableview on the device the value of the button change and I can see it be modified in live.
Did you see this issue before ?
Thanks for your help !
Alex.
Without more details it is difficult to debug. However, if you are using dynamic cells then any off screen cells are candidates for being recycled. If you use static cells, then any off screen cells are retained (not recycled). It is possible that off screen cells are being reused (assuming you are using dynamic cells) producing the behavior you described. If you are having issues with your cellForRowAtIndexPath method then that could be the issue too. Please share that code.
Also, looking at your initial code, you might want consider a dynamic prototype cell that includes the button in IB. Then all you need to do is reference the cell within cellForRowAtIndexPath, grab the button of the cell and set its title via the cell indexPath (i.e. cell 0 gets title 0, cell 1 gets title 1, etc.) -- would save you a bit of code.
I'm facing a paramount problem that led me almost to toss my computer out of the window.
I'm trying to create a button only on some cells, I don't have any problem about triggering the action, but when I scroll down the table and I come back to the first cells the button is created on other cells. In other words, if cells 1 and 3 are supposed to have the button, when the tableview is created they are the only ones having the button. When I scroll down and up again also cell 2, 3 and 4 have the button (there is not a specific rule). The button is also working perfectly but it is not supposed to be there!
static NSString *CellIdentifier = #"Cell";
OpinionCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell= [[OpinionCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
...some influent code.......
if(([[aComment objectForKey:#"TypeMsg"] intValue]==310)&&([[parentMessage objectForKey:#"TypeMsg"] intValue]==310)){
UIButton *_openReplyButton = [[UIButton alloc] initWithFrame:CGRectMake(280, 5, 20, 20)];
[_openReplyButton setImage:[UIImage imageNamed:#"reply_button.png"] forState:UIControlStateNormal];
[_openReplyButton addTarget:self action:#selector(addRowsForShowReply:) forControlEvents:UIControlEventTouchUpInside];
[cell addSubview:_openReplyButton];
NSLog(#"%#", [aComment objectForKey:#"Message"]);
}
Thank you very much for your help!
This is a classic problem for UITableView. It took me ages to figure out the dequeuing and reusing process that the table view does. Here's what should fix it.
Move the code to initialize the button into the part that checks whether cell == nil. This is because you're not supposed to add subviews to cells that have just been dequeued, because you don't know whether that cell has already had the subview added to it. Also, you should either set a tag for the button or make it a property of the OpinionCell. That way you can access it later.
Then, if you have determined that the button should be visible, set cell.replyButton.hidden = NO or [cell viewWithTag:kMyButtonTag].hidden = NO. Very importantly, you should set it to be hidden in an else clause. Otherwise, the button will show on seemingly random cells.
Hope this helps!
You can use following code to remove the subviews from UITableViewCell right after when the
cell is dequeued or initialised as it will remove all its subviews or you can follow what dado728 has mentioned above.
[[cell subviews] performSelectorOnMainThread:#selector(removeFromSuperview) withObject:nil waitUntilDone:NO];
I have setup a tableview that currently has one section, header, and two cells. For username and password, image shown below.
I would like to add a button (IBOutlet), and a label/link below this button within the Table View. What is the correct way to approach this programatically as I am not using interface builder at the moment.
Should the button be a new cell and section? Should the button be a header view and if so how do I control the height on only this header view?
you can try this inside your cell
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
where index path.row == 3
UIButton *btn = [UIButton buttonWithType:UIButtonTypeRoundedRect];
btn.frame = CGRectMake(cell.contentView.frame.size.width/2,10,100,20);
[btn setTitle:#"Login" forState:UIControlStateNormal];
[btn addTarget:self action:#selector(btnPressed:) forControlEvents:UIControlEventTouchUpInside];
[cell.contentView addSubview:btn];
sorry for naming convention
I would suggest just adding another row to your table. If you add a second section with only one row the grouped tableview style will make it look like a simple button. If you need more complex styling you can of course create a custom UITableViewCell with one or more UIImageView(s).
Regarding the actually touch event I would then just stick to the tableView:didSelectRowAtIndexPath: delegate method.
If you have only one touch event for the whole UITableViewCell there isn't really a need to add a UIButton.
I would suggest to use simple text fields instead of a table view. However, if you want the table view approach, you should use another section, and make that button a table cell.