I've got a UITableView containing three sections.
Section 1 has one cell containing a UISwitch failAtLaunch in its accesoryview
Section 2 contains the items from the failSounds array and has a singular checkmark for the selected item (sound in this case)
Section 3 contains a button.
You can see below how I configure the cells..
The problem is that the table gives strange behaviour. Whenever I scroll up and down the tableview, with cells moving in and out of view, some cells in section 2 get the UISwitch failAtLaunchin their accesoryview.
Can anyone help me understand why this is?
Thanks in advance!
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//....blablabla...
// Configure the cell...
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
NSString *theSound = [prefs objectForKey:#"pickedFailSound"];
NSUInteger index = [failSounds indexOfObject:theSound];
if (indexPath.section == 0){
failAtLaunch = [[[UISwitch alloc] initWithFrame:CGRectMake(200, 7, 100, 30)] autorelease];
[cell addSubview:failAtLaunch];
cell.accessoryView = failAtLaunch;
cell.selectionStyle = UITableViewCellSelectionStyleNone;
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
if(![prefs boolForKey:#"failAtLaunch"]) {
[failAtLaunch setOn:NO animated:NO];
} else {
[failAtLaunch setOn:YES animated:NO];
}
[(UISwitch *)cell.accessoryView addTarget:self action:#selector(setIt) forControlEvents:UIControlEventValueChanged];
cell.textLabel.text = #"Instafail";
return cell;
} else if (indexPath.section == 1) {
NSString *cellTitle = [failSounds objectAtIndex:indexPath.row];
cell.textLabel.text = cellTitle;
if (indexPath.row == index) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
return cell;
} else {
cell = [tableView dequeueReusableCellWithIdentifier:#"buttonCell"];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:#"buttonCell"] autorelease];
}
cell.textLabel.text = #"Hold to record fail sound";
UIButton *btn = [UIButton buttonWithType:UIButtonTypeRoundedRect];
btn.frame = CGRectMake(230.0f, 4.0f, 60.0f, 36.0f);
[btn setTitle:#"OK" forState:UIControlStateNormal];
[btn setTitle:#"OK" forState:UIControlStateSelected];
[btn addTarget:self action:#selector(recordAudio) forControlEvents:UIControlEventTouchDown];
[btn addTarget:self action:#selector(stop) forControlEvents:UIControlEventTouchUpInside];
[cell.contentView addSubview:btn];
return cell;
}
}
The problem is from the dequeueReusableCellWithIdentifier: use different static strings for each type of cell.
It's your blah blah blah section. I think you are probably using the same reuse-identifier for all 3 types of rows, and not clearing them out prior to re-use.
So when you instantiate a new tableViewCell, you need to switch on section and give each one a unique identifier.
What I usually do is tag every view that I add to a cell with a tag like "CLEAR_ME_OUT" that is defined as some large arbitrary number. Then when I re-use a cell, I loop through the sub-views and removeFromSuperview all cells with that tag. That way it's nice a clean for re-use.
Related
I have a table view and in each row there is a add button on click the new row adding below i want that onclick the row has one text field in it and a user can enter a texh on it can you help me how to add a text field in the adding row.
here is my code
-(void)buttonclicked:(UIButton *)sender
{
UIButton *btn = (UIButton *)[self.view viewWithTag:sender.tag];
NSLog(#"clickedCell=%i", btn.tag);
[[NSUserDefaults standardUserDefaults]setValue:[NSString stringWithFormat:#"%i", btn.tag] forKey:#"btntag"];
[self.choices insertObject:#"newrow" atIndex:btn.tag+1];
[self.tableView reloadData];
}
and the table view method
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
UIButton *button = [UIButton buttonWithType:UIButtonTypeContactAdd];
button.tag = indexPath.row;
button.frame = CGRectMake(280.0, 10, 25, 30.0); // x,y,width,height
[button addTarget:self action:#selector(buttonclicked:) forControlEvents:UIControlEventTouchUpInside];
[cell addSubview:button];
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:cell action:nil];
longPress.delegate = self;
[cell addGestureRecognizer:longPress];
int count = 0;
if(self.editing && indexPath.row != 0)
count = 1;
NSLog([NSString stringWithFormat:#"%i,%i",indexPath.row,(indexPath.row-count)]);
// Set up the cell...
if(indexPath.row == ([_choices count]) && self.editing)
{
cell.textLabel.text = #"ADD";
return cell;
}
NSString *choice = [self.choices objectAtIndex:indexPath.row];
cell.textLabel.text = choice;
return cell;
}
try like this may be it helps to you,
1.take one global variable type integer for example position.
2.when you click on particular button -(void)buttonclicked:(UIButton *)sender method will call in this method you will assign the btn.tag value to the position .
3.after that you are reloading the table view in the tableview cellForRowAtIndexPath method take one condition like
if(position==indexpath.row)
{
//add your textfield here
}.
I have seen some posts before, but didn't get the answer yet, thats why i am trying to post again in more effective manner. How can i use check-uncheck functionality in UITableView like below image.
This is table i want when i click on button of any cell, that buttons image will change, not on all cells.
For Check-Uncheck functionality only buttonClicked: method is not enough. You will have also put the condition in cellForRowAtIndexPath: method for which button is selected or which in unselected because cellForRowAtIndexPath: method will call each time when you will scroll your UITableView and cells will be refresh.
And i saw your previous question you're adding two buttons with two action not a good way just change the image of button for check-uncheck.
So here is what i do for this -
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController<UITableViewDelegate, UITableViewDataSource>
{
IBOutlet UITableView *tblView;
NSMutableArray *arrayCheckUnchek; // Will handle which button is selected or which is unselected
NSMutableArray *cellDataArray; // this is your data array
}
#end
Now in ViewController.m class -
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
arrayCheckUnchek = [[NSMutableArray alloc]init];
//Assign your cell data array
cellDataArray = [[NSMutableArray alloc]initWithObjects:#"cell-1",#"cell-2",#"cell-3",#"cell-4",#"cell-5", nil];
// setting all unchecks initially
for(int i=0; i<[cellDataArray count]; i++)
{
[arrayCheckUnchek addObject:#"Uncheck"];
}
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return [cellDataArray count];
}
-(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];
}
cell.textLabel.text = [cellDataArray objectAtIndex:indexPath.row];
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
[button setFrame:CGRectMake(270.0, 7.0, 30.0, 30.0)];
if([[arrayCheckUnchek objectAtIndex:indexPath.row] isEqualToString:#"Uncheck"])
[button setImage:[UIImage imageNamed:#"uncheck_icon"] forState:UIControlStateNormal];
else
[button setImage:[UIImage imageNamed:#"check_icon"] forState:UIControlStateNormal];
[button addTarget:self action:#selector(buttonClicked:) forControlEvents:UIControlEventTouchUpInside];
[cell.contentView addSubview:button];
return cell;
}
-(void)buttonClicked:(id)sender
{
//Getting the indexPath of cell of clicked button
CGPoint touchPoint = [sender convertPoint:CGPointZero toView:tblView];
NSIndexPath *indexPath = [tblView indexPathForRowAtPoint:touchPoint];
// No need to use tag sender will keep the reference of clicked button
UIButton *button = (UIButton *)sender;
//Checking the condition button is checked or unchecked.
//accordingly replace the array object and change the button image
if([[arrayCheckUnchek objectAtIndex:indexPath.row] isEqualToString:#"Uncheck"])
{
[button setImage:[UIImage imageNamed:#"check_icon"] forState:UIControlStateNormal];
[arrayCheckUnchek replaceObjectAtIndex:indexPath.row withObject:#"Check"];
}
else
{
[button setImage:[UIImage imageNamed:#"uncheck_icon"] forState:UIControlStateNormal];
[arrayCheckUnchek replaceObjectAtIndex:indexPath.row withObject:#"Uncheck"];
}
}
And finally it will look like -
Tags are useful with customised uitableviewcell designed in IB. If you create cells programmatically, you don't need tags. You use them only to find correct uiview to set properties.
Yes this is simple to set Tags while declaring the classes inside UITableViewCell and identify them using tags. I have posted some sample code for your reference. Am not sure it will solve your problem. You asked how to set tags and identify the classes using tags. So it will give you some basic idea about your question.
-(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];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
cell.selectionStyle = UITableViewCellSelectionStyleNone;
label1 = [[UIView alloc] initWithFrame:CGRectMake(2, 20, 15, 15)];
label1.tag = 100;
label1.backgroundColor = [UIColor whiteColor];
label1.layer.cornerRadius = 5;
[cell.contentView addSubview: label1];
label2 = [[UILabel alloc] initWithFrame:CGRectMake(25, 2, 165, 23)];
label2.tag = 101;
label2.font = [UIFont boldSystemFontOfSize:15];
label2.backgroundColor = [UIColor clearColor];
[cell.contentView addSubview: label2];
label3 = [[UILabel alloc] initWithFrame:CGRectMake(190, 2, 90, 23)];
label3.tag = 102;
label3.font = [UIFont systemFontOfSize:14];
label3.textColor = [UIColor colorWithRed:0.14117670588235 green:0.435294117647059 blue:0.847058823529412 alpha:1];
label3.backgroundColor = [UIColor clearColor];
label3.textAlignment = UITextAlignmentRight;
[cell.contentView addSubview: label3];
}
label1 = (UILabel *) [cell.contentView viewWithTag:100];
NSString *string1 = [array1 objectAtIndex:indexPath.row];
label2 = (UILabel *) [cell.contentView viewWithTag:101];
NSString * string2 = [array2 objectAtIndex:indexPath.row];
label3 = (UILabel *) [cell.contentView viewWithTag:102];
NSString * string3 = [array3 objectAtIndex:indexPath.row];
return cell;
}
Please let me know this is useful or not. Otherwise we'll go to some other way.
Happy Coding. Thanks.
i agree with #VakulSaini because u can do this to handle which cell touch or swipe or what ever and this is an ex:
-(void)handleSwipLeft:(UISwipeGestureRecognizer *)gestureRecognizer
{
CGPoint Location = [gestureRecognizer locationInView:self.tableView];
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:Location];
cell = [tableView cellForRowAtIndexPath:indexPath];
}
Use cellForRowAtIndexPath method of UITableView and in This method Add any component like (UITextField,UILabel ..... etc ) In your UITableViewCell by using [cell.contentView addSubview:YourComponentName]; and give each component to its tag by indexPath.row.
such like ,,,
YourComponentName.tag = indexPath.row;
I have a checkbox class that I want to toggle a boolean core data attribute from NO to YES. Drawing the actual checkbox and getting it to toggle isn't the issue, but persisting its state is. The checkbox is in the second view controller in my stack (RVC <--> ItemsVC (checkboxes are here) <--> ItemDetailVC). If I toggle the checkbox and then navigate either way, the state returns to unchecked.
I've tried a few ways to a) make the checkbox state persist and to b) make the state of the checkbox change the state of my boolean attribute from NO to YES.
- (id)initWithFrame:(CGRect)frame {
if ((self = [super initWithFrame:frame])) {
// Initialization code
self.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;
[self setImage:[UIImage imageNamed:#"unchecked.png"] forState:UIControlStateNormal];
[self setImage:[UIImage imageNamed:#"checked.png"] forState:UIControlStateSelected];
[self addTarget:self action: #selector(checkboxTapped) forControlEvents:UIControlEventTouchUpInside];
}
return self;
}
- (IBAction) checkboxTapped {
if (self.isChecked == NO) {
self.isChecked = YES;
[self setImage:[UIImage imageNamed:#"checked.png"] forState:UIControlStateNormal];
self.selectedItem.inCart = isChecked;
} else {
self.isChecked = NO;
[self setImage:[UIImage imageNamed:#"unchecked.png"] forState:UIControlStateNormal];
}
}
isChecked is a BOOL that's declared in the header and selectedItem.inCart are my core data entity and boolean attribute, respectively.
EDIT: Here's the code in ItemsVC where the checkbox appears.
- (UITableViewCell *)tableView:(UITableView *)IVCTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [IVCTableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
Item *item = [fetchedResultsController_ objectAtIndexPath:indexPath];
[self configureCell:cell withItem:item];
UILabel *itemLabel = [[UILabel alloc] initWithFrame:CGRectMake(50, 0, 100, 40)];
[cell.contentView addSubview:itemLabel];
itemLabel.text = item.itemName;
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
Checkbox *checkbox = [[Checkbox alloc] initWithFrame:CGRectMake(10, 0, 70, 50)];
[cell.contentView addSubview:checkbox];
return cell;
}
EDIT 2: Here's the current state of my code, with comments about what works and what doesn't. At this point, the checkbox will check and uncheck, but the check will repeat every 10 rows, which I assume is where dequeueReusableCellWithIdentifier is doing its thing.
- (id)initWithFrame:(CGRect)frame {
if ((self = [super initWithFrame:frame])) {
// Initialization code
self.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;
// If I don't set these images here, they don't appear in the table
[self setImage:[UIImage imageNamed:#"unchecked.png"] forState:UIControlStateNormal];
[self setImage:[UIImage imageNamed:#"checked.png"] forState:UIControlStateSelected];
[self addTarget:self action: #selector(checkboxTapped) forControlEvents:UIControlEventTouchUpInside];
}
return self;
}
// This function prevents checked checkboxes from unchecking
/*
- (void)setIsChecked:(BOOL)isChecked {
if (isChecked == isChecked_)
return;
[self setImage:[UIImage imageNamed:isChecked_?#"checked.png":#"unchecked.png"] forState:UIControlStateNormal];
self.selectedItem.inCart = [NSNumber numberWithBool:isChecked_];
}
*/
- (IBAction) checkboxTapped {
if (self.isChecked == NO) {
self.isChecked = YES;
// If I comment this out, the checked.png doesn't render
[self setImage:[UIImage imageNamed:#"checked.png"] forState:UIControlStateNormal];
self.selectedItem.inCart = [NSNumber numberWithBool:isChecked_];
// I commented this out because for some reason it prevents the checkbox from unchecking
//self.isChecked = [[selectedItem_ inCart] boolValue];
} else {
self.isChecked = NO;
// If I comment this out, once the checkbox is checked, it won't uncheck
[self setImage:[UIImage imageNamed:#"unchecked.png"] forState:UIControlStateNormal];
self.selectedItem.inCart = [NSNumber numberWithBool:isChecked_];
self.isChecked = [[selectedItem_ inCart] boolValue];
}
}
I left cellForRowAtIndexPath as #imaginaryboy suggested, except I used checkBox.isChecked = [[item inCart] boolValue]; as #westsider suggested. While this seems cleaner than what I was doing before, I'm not quite sure how to handle my original issue. I've been re-reading the docs, yet remain at a loss. Is there a way to check that inCart is actually getting its state changed? As for the checked cells, should I be caching them, or writing cells to a plist, or giving each cell its own unique identifier at creation (as I'm writing this, that seems like the way to go. Yes?)?
It occurs to me that if I was really successful in changing the state of inCart (which is a core data attribute), shouldn't its state be saved automatically when the checkbox gets checked, assuming that checkBox.isChecked = [[item inCart] boolValue]; works?
Firstly, as I commented on a little while ago, you're reusing cells but adding a new UILabel and Checkbox every time a cell is reused in addition to when it's first created.
To sort that out, something like the following:
- (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];
UILabel *itemLabel = [[UILabel alloc] initWithFrame:CGRectMake(50, 0, 100, 40)];
itemLabel.tag = 1;
[cell.contentView addSubview:itemLabel];
Checkbox *checkbox = [[Checkbox alloc] initWithFrame:CGRectMake(10, 0, 70, 50)];
checkbox.tag = 2;
[cell.contentView addSubview:checkbox];
}
Item *item = [fetchedResultsController_ objectAtIndexPath:indexPath];
[self configureCell:cell withItem:item];
UILabel *itemLabel = (UILabel*)[cell viewWithTag:1];
itemLabel.text = item.itemName;
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
Checkbox *checkbox = (Checkbox*)[cell viewWithTag:2];
checkbox.isChecked = YES or NO based on item.inCart??
return cell;
}
Though actually rather than setting the itemLabel.text and checkbox state here, perhaps the method configureCell:withItem: that you're calling could/should do that?
With respect to the issue you're having where the state isn't preserved between navigations, it may be simply that you're adding this new Checkbox to the cell every time and so it's appearing in whatever it's default checked/unchecked state is.
Also, you may want to try something like the following in Checkbox:
- (void)setIsChecked:(BOOL)isChecked {
if (isChecked_ = isChecked)
return;
[self setImage:[UIImage imageNamed:isChecked_?#"checked.png":#"unchecked.png"] forState:UIControlStateNormal];
self.selectedItem.inCart = [NSNumber numberWithBool:isChecked_];
}
- (IBAction) checkboxTapped {
if (self.isChecked == NO) {
self.isChecked = YES;
} else {
self.isChecked = NO;
}
}
EDIT: modified assigment of self.selectedItem.inCart as per #westsider's comment
You are going to need some code somewhere that actually sets the checkbox image based on the value of selectedItem.inCart. My guess is that inside ItemsVC you will want to implement -viewWillAppear: or -viewDidAppear: and iterate through your checkboxes, setting them as would be appropriate.
Without seeing more of your code, I can't suggest any more than this.
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...
I am trying to have a button for selected rows of my table.
Here is the example code I am using:
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *ControlRowIdentifier = #"ControlRowIdentifier";
UITableViewCell *cell = [tableView
dequeueReusableCellWithIdentifier:ControlRowIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:ControlRowIdentifier] autorelease];
}
if ([indexPath row] > 5) {
UIImage *buttonUpImage = [UIImage imageNamed:#"button_up.png"];
UIImage *buttonDownImage = [UIImage imageNamed:#"button_down.png"];
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.frame = CGRectMake(0.0, 0.0, buttonUpImage.size.width, buttonUpImage.size.height);
[button setBackgroundImage:buttonUpImage forState:UIControlStateNormal];
[button setBackgroundImage:buttonDownImage forState:UIControlStateHighlighted];
[button setTitle:#"Tap" forState:UIControlStateNormal];
[button addTarget:self action:#selector(buttonTapped:) forControlEvents:UIControlEventTouchUpInside];
cell.accessoryView = button;
}
NSUInteger row = [indexPath row];
NSString *rowTitle = [list objectAtIndex:row];
cell.textLabel.text = rowTitle;
return cell;
}
This code works absolutely fine when loaded for 1st time. So, as per the logic it shows 'Tap' button for all rows greater than 5.
Problem occurs when I scroll up and down. Once I do that it just starts putting that button at any random row. I dont understand why it does that and it'll be really helpful if someone can give some tips on this.
Thanks.
The problem is reusing cell's identifier. In your case, the cell with an index of less than 6 must be with one identifier, and the rest from other.
Table view cells is reusable objects and you must do some clean work with it. Try use next:
if ([indexPath row] > 5) {
...
} else {
cell.accessoryView = nil;
}