iPhone question: How can I add a button to a tableview cell? - iphone

Little background, the table view is filled by a fetchedResultsController which fills the table view with Strings. Now I'm trying to add a button next to each String in each tableview cell. So far, I've been trying to create a button in my configureCell:atIndexPath method and then add that button as a subview to the table cell's content view, but for some reason the buttons do not show up. Below is the relevant code. If anyone wants more code posted, just ask and I'll put up whatever you want. Any help is greatly appreciated.
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath {
// get object that has the string that will be put in the table cell
Task *task = [fetchedResultsController objectAtIndexPath:indexPath];
//make button
UIButton *button = [[UIButton buttonWithType:UIButtonTypeRoundedRect] retain];
[button setTitle:#"Check" forState:UIControlStateNormal];
[button setTitle:#"Checked" forState:UIControlStateHighlighted];
//set the table cell text equal to the object's property
cell.textLabel.text = [task taskName];
//addbutton as a subview
[cell.contentView addSubview:button];
}
- (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] autorelease];
}
// Configure the cell.
[self configureCell:cell atIndexPath:indexPath];
return cell;
}

Three comments:
You'll probably need to set the frame of your UIButton. Otherwise it doesn't know how big it's supposed to be and where it's supposed to be placed. It's possible that it comes with a default frame, but I haven't investigated that.
You're leaking your buttons. You don't need to retain after calling [UIButton buttonWithType:...];
You have the possibility of adding multiple buttons to the same cell. If you're reusing a cell, then you should first invoke removeFromSuperview each subview of cell.contentView .

Related

I have a drawer of buttons in UITableViewCell. How do I get the indexPath of the cell and delete it?

I have implemented this button drawer and I've added some buttons to it. That said, I'm unsure how I'll go about sending messages from these buttons in the drawer to the appropriate delegate method to delete the item from my tableView.
How do I get the correct indexPath? Should I make a new NSIndex for uitableviewcells that have had their check button toggled?
Thanks
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
HHPanningTableViewCell *cell = (HHPanningTableViewCell*)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
NSInteger directionMask = indexPath.row % 5;
if (cell == nil) {
cell = [[HHPanningTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
UIView *drawerView = [[UIView alloc] initWithFrame:cell.frame];
drawerView.backgroundColor = [UIColor grayColor];
cell.drawerView = drawerView;
UIImage *checkImage = [UIImage imageNamed:#"check.png"];
UIButton *checkButton = [UIButton buttonWithType:UIButtonTypeCustom];
[checkButton setImage:checkImage forState:UIControlStateNormal];
cell.frame = CGRectMake(0, 0, cell.frame.size.width, cell.frame.size.height);
checkButton.frame = CGRectMake(10, 10, checkImage.size.width, checkImage.size.height);
[drawerView addSubview:checkButton];
[checkButton addTarget:nil action:#selector(onCheckMarkTap:) forControlEvents:UIControlEventTouchUpInside];
}
- (void)onCheckMarkTap {
NSLog(#"Delete the cell");
}
-(void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
}
}
This question already has an answer. You get the coordinates of the button that was tapped in the table view's coordinate system using target/action with an event or via a gesture recognizer, then use UITableView's method -indexPathForRowAtPoint: to get the indexPath of the cell containing the button at that point.
Within cellForRowAtIndexPath:
checkButton.tag = indexPath.row
Also inside onCheckMarkTap, do this:
- (void)onCheckMarkTap : (id) sender
{
if (sender.tag == 0)
///code
else if (sender.tag == 1)
///code
}
The way I handle problems like this in my own code is to subclass "UIButton", where I add in a "NSIndexPath" or "tableRow" ivar into the subclassed button. Let's name is "StangButton".
With a subclassed button, you can populate the "indexPath" ivar when you are composing the cell via "cellForRowAtIndexPath:".
Then, when the button is pressed, you'll know which button was pressed and which row it was in.
You can also check to see if the "tableView:didSelectRowAtIndexPath:" table view delegate method is called when the button is touched in the cell.

How to change the position of a UISwitch in a TableView

I have a TableView that has UISwitches in it. The switches are slightly off the screen. how do I make the display correctly.
Here is my code that displays the switches.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *CellIdentifier = #"POICell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
if(!cell){
//set the cell text to the
cell.textLabel.text = [self.catNames objectAtIndex:[indexPath row]];
NSString *toggle = [self.toggleArray objectAtIndex:[indexPath row]];
//add switch
cell.selectionStyle = UITableViewCellSelectionStyleNone;
UISwitch *switchView = [[UISwitch alloc] initWithFrame:CGRectZero];
cell.accessoryView = switchView;
if ([toggle isEqualToString: #"OFF"]) {
[switchView setOn:NO animated:NO];
}else{
[switchView setOn:YES animated:NO];
}
[switchView addTarget:self action:#selector(switchChanged: ) forControlEvents:UIControlEventValueChanged];
// Configure the cell...
}
return cell;
}
Rather than adding a UISwitch to the standard cell, I'd create a custom UITableViewCell that contained the text label and the switch.
But after looking at the Apple docs, it seems what you want to achieve is a valid approach.
Table View Programming Guide for iOS
And your code already looks a lot like this SO question: How to create a UITableViewCell with a UISwitch and get the data? So I am not sure why the UISwitch isn't displaying as expected.
I think the position of the default accessoryView can't be moved without a problem, as the TableViewCell will reposizion it in many situations.
So adding it as a subview might be the better idea, instead of trying to "hack" the default behavior.

How to generate a UITextField and UIButton in UIScrollView and remove these items dynamically

HI stackoverflow friends
I have situation that when i click on a button it will generate dynamically a uitextfield with a remove button. If I don't need that text field then i click the remove button it will goes off.More over the user can generate infinite no: of these pair. So that I think it should be add in a scrollview. Is it possible this. Can anyone know how to do this?
Any help would be appreciable.
In your TableView cellForRowAtIndexpath method:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
UITextField* textField;
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
textField = [[UITextField alloc] initWithFrame:CGRectMake(10,20,30,30)];
textField.tag = 10;
[[cell contentView] addSubView:textField];
[textField release];
}
else
{
textField = (UITextField *)[cell.contentView viewWithTag:10];
}
// Do something with TextField
return cell;
}
You can just have one add button on the navigation bar across top right that would add to NSMutableArray addObject textField to it and reloadTable. Inside cellForRowAtIndexPath get objectAtIndex for that array and add to cell contentView. To delete the cells you can implement swipe to delete for cells...
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
// If row is deleted, remove it from the Array.
if (editingStyle == UITableViewCellEditingStyleDelete)
{
// delete your data item here
}
}
Posted some code that should help you:
// alloc + init + set appropriate size and origin
UIScrollView *scroll;
UITextField *field;
UIButton *button;
// add subview
[scroll addSubview:field];
[scroll addSubview:button];
// remove subview
[field removeFromSuperview];
[button removeFromSuperview];
// memory managment
[field release];
[button release];

UITableView with multiple items in each cell

I have a tableview in which I am trying to place a button with an image and a label. I want to change the image of the button once clicked.
Here's the code:
- (UITableViewCell *)tableView:(UITableView *)_tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
checkedImg = [UIImage imageNamed:#"buttonUnChecked1.png"];
UITableViewCell *cell = [_tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
}
//Set up the cell...
NSString *cellValue = [suggestions objectAtIndex:indexPath.row];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
check = [UIButton buttonWithType:UIButtonTypeCustom];
check.frame=CGRectMake(0,35,20,20);
[check setImage:checkedImg forState:UIControlStateNormal];
[check addTarget:self action:#selector(checkClicked:) forControlEvents: UIControlEventTouchUpInside];
[cell.contentView addSubview:check];
cellContent = [[UILabel alloc]initWithFrame:CGRectMake(40,32,500,25)];
cellContent.text = cellValue;
[cell.contentView addSubview:cellContent];
return cell;
}
-(void)checkClicked:(UIButton *)b
{
checkedImg = [UIImage imageNamed:#"buttonChecked1.png"];
[check setImage:checkedImg forState:UIControlStateNormal];
}
By doing this, the image of the buttons are getting changed but only the last one and not the one clicked. I know the reason behind it, but I don't know how to achieve what I want.
A structured way to get the result you're looking for:
Make a UIView subclass that for your table cells (containing a button and label). You instantiate these custom views and set them as your contentView for each table cell in cellForRowAtIndexPath.
Each of your custom views listens for its own button being pressed. When it was pressed, it toggles its state and tells the main viewcontroller (via a delegate method) that it was toggled. The main view controller calls reloadData on the cell in question to cause it to be reloaded with the correct appearance.
Note that this approach requires you to tell each of the custom views which index path it is rendering for in the table -- that way it can inform the main view controller's delegate method -- this info is needed for triggering a reload of the appropriate cell.
Btw, I presume you want to look at the state of the buttons in your table when the user is done with editing, and your current approach doesn't capture the state stuff very explicitly -- you'd have to iterate over your buttons, or add selected items to a mutable array, or something.
The easy answer to your problem is to change your checkClicked: method to this:
-(void)checkClicked:(UIButton *)b
{
[b setImage:[UIImage imageNamed:#"buttonChecked1.png"] forState:UIControlStateNormal];
}
But you should also adjust your tableView:cellForRowAtIndexPath: method to avoid creating the button repeatedly and to clean up some memory issues like this:
- (UITableViewCell *)tableView:(UITableView *)_tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [_tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
UIButton *checkBtn = [UIButton buttonWithType:UIButtonTypeCustom];
checkBtn.frame = CGRectMake(0,35,20,20);
[checkBtn setImage:[UIImage imageNamed:#"buttonUnChecked1.png"]; forState:UIControlStateNormal];
[checkBtn addTarget:self action:#selector(checkClicked:) forControlEvents: UIControlEventTouchUpInside];
[cell.contentView addSubview:checkBtn];
UILabel *cellContentLbl = [[UILabel alloc]initWithFrame:CGRectMake(40,32,500,25)];
cellContentLbl.tag = 1;
[cell.contentView addSubview:cellContentLbl];
[cellContentLbl release];
}
//Set up the cell...
NSString *cellValue = [suggestions objectAtIndex:indexPath.row];
cellContent = (UILabel *)[cell viewWithTag:1];
cellContent.text = cellValue;
return cell;
}

Button on uitableview

I'm having a problem with button on uitableview. As in the code, when i click on the button of each cell the image Checked.png should be shown on the button. THis is working fine. However, when i scroll the tableview, the checked image on top of the table disappear.How do i solve this. Can any kind person please help me on this?
//table.m//
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
//- (UITableViewCell *)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
[[NSBundle mainBundle] loadNibNamed:#"TableCell" owner:self options:NULL];
cell = nibLoadedCell;
button *toggleControl = [[CheckBox alloc] initWithFrame: CGRectMake(270.0, 10.0, 60.0, 60.0)];
toggleControl.tag = toggleControlTag;
[cell.contentView addSubview: toggleControl];
[toggleControl addTarget:self action:#selector(Selected:) forControlEvents:UIControlEventTouchDown];
}
//button.m//
- (id)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
// Initialization code
self.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;
[self setImage:[UIImage imageNamed:#"Uncheck.png"] forState:UIControlStateNormal];
[self addTarget:self action:#selector(checkBoxClicked) forControlEvents:UIControlEventTouchUpInside];
}
return self;
}
-(IBAction) checkBoxClicked{
if(self.isChecked ==NO){
self.isChecked =YES;
[self setImage:[UIImage imageNamed:#"Checked.png"] forState:UIControlStateNormal];
}else{
self.isChecked =NO;
[self setImage:[UIImage imageNamed:#"Uncheck.png"] forState:UIControlStateNormal];
}
}
You are storing the state for the button in the button. That is the wrong approach. The button is only the view for the state. You need to store your data elsewhere, e.g. in an array, and pull the relevant data for a table cell from the array depending on the indexPath for that cell.
When someone presses your button, you can, e.g., set a BOOL in your data model to indicate that this particular option was selected by the user.
Your cells will always scroll off the view, which will lead to the cells being reused, and your button will no longer be "valid". So you need to check, for every cell that you create, what data you have in your model for this particular part of your data, and fill in the view (= the cell) accordingly.
It wouldn't harm to read up on design patterns.