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.
Related
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.
I have a tableViewController, the cells contains a button and a label. I need to get the text of the cell (actually the object of the cell Person) when the user clicks on the button.
When the user clicks on the button the following method gets executed;
-(void) buttonOfCellClicked{
// here i need to access the `Person` object that the user clicked
}
How do i write this code?
EDIT:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
Person *person = [personsArr objectAtIndex:indexPath.row];
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] ;
label = [[UILabel alloc]initWithFrame:CGRectMake(15, 5, 75, 60)];
label.text =person.firstName;
[cell addSubview:label];
UIButton *button= [UIButton buttonWithType:UIButtonTypeRoundedRect];
[button addTarget:self action:#selector(buttonOfCellClicked) forControlEvents:UIControlEventTouchUpInside];
[cell.contentView addSubview:button];
}
id findAncestor(UIView *view, Class class) {
while (view && ![view isKindOfClass:class])
view = [view superview];
return view;
}
- (void)buttonOfCellClicked:(UIButton *)button
{
UITableViewCell *cell = (UITableViewCell *)findAncestor(button, [UITableViewCell class]);
UITableView *table = (UITableView *)findAncestor(cell, [UITableView class]);
NSIndexPath *path = [table indexPathForCell:cell];
if (!path)
return;
Person *person = [personsArr objectAtIndex:path.row];
// do whatever with person
}
I would subclass UITableviewCell and add a property and the action to that class and in IB connect the button. When you are configuring the cell just set the person object. This approach would depend heavily on what you plan on doing with that object.
You need to ask your button to tell you where it is. To achieve this, you need to call superview twice (1x will return cell.contentView, 2x will return cell):
- (void)buttonOfCellClicked:(UIButton *)sender {
UITabvleViewCell *cell = (UITableViewCell *)[[sender superview] superview];
}
Then you can access cell's properties e.g. cell.textLabel.text
EDIT: You also need to add : to your -addTarget:action:forControlEvents: in cellForRow after buttonOfCellClicked text:
[button addTarget:self action:#selector(buttonOfCellClicked:) forControlEvents:UIControlEventTouchUpInside]
EDIT2: you can access person by
NSIndexPath *ip = [yourTableView indexPathForCell:cell];
Person *p = [personArr objectAtIndex:ip.row];
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return [yourArray count];\
}
- (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] ;
UIButton *button= [UIButton buttonWithType:UIButtonTypeRoundedRect];
[button setTag:indexPath.row]; // SET TAG TO YOUR BUTTON, IT WILL BE SAME AS YOUR OBJECT AT INDEX OF ARRAY
[button addTarget:self action:#selector(buttonOfCellClicked:) forControlEvents:UIControlEventTouchUpInside];
[cell.contentView addSubview:button];
}
-(void) buttonOfCellClicked:(id) sender{
Person *person = [yourArray objectAtIndex:[sender tag]];
// here i need to access the `Person` object that the user clicked
}
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;
}
Please look at my following code snippet. I place check box in to my cell , i can able to check or uncheck the checkbox. but when i scroll the table view the checkbox's check mark is getting hidden.
i mean i set the check mark by setting the image.
Please anybody help me to solve this problem.
Thanks for your time.
// Customize the appearance of table view cells.
- (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:#"en"] autorelease];
cb = [[UIButton alloc] initWithFrame:CGRectMake(5,10, unselectedImage.size.width, unselectedImage.size.height)];
[cb setImage:unselectedImage forState:UIControlStateNormal];
[cb setImage:selectedImage forState:UIControlStateSelected];
[cb addTarget:self action:#selector(buttonAction:) forControlEvents:UIControlEventTouchDown];
[cell.contentView addSubview:cb];
}
if ( tableView == myTableView )
{
titleLabel = [[UILabel alloc]initWithFrame:CGRectMake(60, 0, 150, 35)];
titleLabel.font = [UIFont boldSystemFontOfSize:13];
titleLabel.textColor = [UIColor blackColor];
[cell.contentView addSubview:titleLabel];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
return cell;
}
Taken from here
If I understand the TableView concept correctly, you need to save the checkmark setting somewhere else. If your cell gets scrolled out of view the TableViewController might release the cell. Then, when you're asked to re-create the cell in your delegate, you have to restore it in the same state as before.
In DidSelectRowAtindexPath:
if ([set containsObject:indexPath])
{
[set removeObject:indexPath];
}
else
{
[set addObject:indexPath];
}
and in CellForRpwAtIndexPath:
if ([set containsObject:indexPath])
{
cell.imgView.image=[UIImage imageNamed:#"chealBox1.png"];
}
else
{
cell.imgView.image=[UIImage imageNamed:#"chealBox2.png"];
}
here set is NSMutableSet and you have to make property and synthesize and alloc as well in viewDidLoad...will work definitely...
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 .