Button pressed in Table View Cell with reusable cells - xcode

I'm struggling with the following problem.
I've got a table view of options for a boat. Each option (cell) has a name, a price and a button that indicates whether the option has been selected or not (see attached screenshot)-
The problem is that when I tap on a couple of options from Section 1, I get the button_pressed state also for cells in the last section. Probably this happens because cells are reused ?
This is the function that gets called when the cell is tapped, to add the option.
- (IBAction)cellButtonSelection:(id)sender {
float prezzoParziale;
UIButton * button = sender;
//NSLog(#"%#",button.superview.superview);
if ([button.superview.superview isKindOfClass:[CellOptions class]] ) {
CellOptions * cellOpt =(CellOptions *)button.superview.superview;
//BOOL selectionState = NO;
cellOpt.selected =! cellOpt.selected;
if(cellOpt.selected){
//UIButton *button = cellOpt.button1;
[button setImage:[UIImage imageNamed:#"checkedButton.png"] forState:UIControlStateNormal];
prezzoParziale = [cellOpt.label2.text floatValue];
[accessoriSelezionati addObject:cellOpt.label1.text];
[accessoriSelezionatiPrezzi addObject: cellOpt.label2.text];
NSLog(#"stampa: %#", accessoriSelezionati);
}else{
//UIButton *button = cellOpt.button1;
[button setImage:[UIImage imageNamed:#"uncheckedButton.png"] forState:UIControlStateNormal];
prezzoParziale = [cellOpt.label2.text floatValue] * -1;
[accessoriSelezionati removeObject:cellOpt.label1.text];
[accessoriSelezionatiPrezzi removeObject: cellOpt.label2.text];
}
[self updateTotal:prezzoParziale]; }
And.....
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
CellOptions * cellOpt;
cellOpt = [tableView dequeueReusableCellWithIdentifier:CellOption forIndexPath:indexPath];
cellOpt.label1.text = [[[accessoriXtipologia objectForKey:tipologia] objectAtIndex:indexPath.row] valueForKey:#"nome"];
cellOpt.label2.text = [[[[accessoriXtipologia objectForKey:tipologia] objectAtIndex:indexPath.row] valueForKey:#"prezzo"] stringValue];
return cellOpt;
}
EDIT: Now it's working
Added in cellForRowAtIndexPath:
cellOpt.selected = [accessoriSelezionati containsObject:cellOpt.label1.text];
if (cellOpt.selected) {
[cellOpt.button1 setImage:[UIImage imageNamed:#"checkedButton.png"] forState:UIControlStateNormal];
}
else {
[cellOpt.button1 setImage:[UIImage imageNamed:#"uncheckedButton.png"] forState:UIControlStateNormal];
}
return cellOpt;

Yes, this is due to cell reuse. In cellForRowAtIndexPath, in addition to setting the labels, you've got to set the selected state by doing something like this:
cellOpt.selected = [accessoriSelezionati containsObject:cellOpt.label1.text];

Related

xcode Converting UITableView to UICollectionView (no valid cell)

EDIT: I should specify that this only happens when I attempt to use the UICollectionViewFlowLayout, not when I try to use a custom view. But either way nothing ever shows up on the CollectionView though it was working just fine before I converted from a TableView.)
So I've been trying to convert a UITableView that I had into a UICollectionView. So far so good. But when I try to run the app it gives me the error:
'the collection view's data source did not return a valid cell from -collectionView:cellForItemAtIndexPath: for index path {length = 2, path = 0 - 0}'
I checked all the similar questions and answers here... so in my viewDidLoad I have (tableView is actually a UICollectionView):
UINib * placeCell = [UINib nibWithNibName:#"Shops" bundle:nil];
[self.tableView registerNib:placeCell
forCellWithReuseIdentifier:CellIdentifier];
#pragma mark - UICollectionViewDataSource
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)tableView
{
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UICollectionView *)tableView numberOfItemsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return [_entries count];
//return 5;
}
- (void)tableView:(UICollectionView *)tableView willDisplayCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.item == [_entries count]-1 && page > 1) {
NSLog(#"load more");
//add footer view loading
if (c_page == page) {
// _tableView.tableFooterView = nil;
}
else
{
c_page++;
[self loadPlace:c_page];
}
}
}
- (UICollectionViewCell *)tableView:(UICollectionView *)tableView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
PlaceCell *cell = (PlaceCell *)[tableView dequeueReusableCellWithReuseIdentifier:CellIdentifier forIndexPath:indexPath];
UINib * placeCell = [UINib nibWithNibName:#"Shops" bundle:nil];
//cell = [cellLoader instantiateWithOwner:self options:nil];
NSArray *topLevelItems = [placeCell instantiateWithOwner:self options:nil];
cell = [topLevelItems objectAtIndex:0];
Place *p = [_entries objectAtIndex:indexPath.row];
cell.placeName.text = p.PName;
NSLog (#"p:%#", p.PName")
cell.placeImg.image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:p.PImage]]];
return cell;
}
I went into the xib of the UICollectionViewCell (PlaceCell) and made sure that "Cell" was the reuseidentifier. And I made sure that the datasource and delegate were connected to file's owner in the collectionView.
I also noticed that when I use a custom layout instead of the flow layout (like this one: https://github.com/ShadoFlameX/PhotoCollectionView/blob/master/CollectionViewTutorial/BHPhotoAlbumLayout.m ) it doesn't give me that error... but my collectionview still isn't populated.
So I'm wondering if there's some sort of log I can run or something I can do to figure out what's going wrong. Because I've tried all the solutions I've seen and it hasn't gotten me anywhere.
When you make a cell in a xib file you should register the xib, not the class. Also, when you register either the class or xib (or make the cell in the storyboard), you don't need an if (cell==nil) clause because your cell will never be nil when you dequeue it with dequeueReusableCellWithReuseIdentifier:forIndexPath:. You should delete that clause.
So the problem is: "Switched from UITableView to UICollectionView and no valid cell is being returned." It is really a two part answer. The crux of which is every instance of UITableView...
_tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 50, self.view.bounds.size.width, self.view.bounds.size.height-50)];
...you want to turn into "CollectionView"
_collectionView = [[UICollectionView alloc] initWithFrame:CGRectMake(0, 50, self.view.bounds.size.width, self.view.bounds.size.height-50)];
Everything that's a "row":
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
...you'll want to turn into an "item."
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
Ultimately I had to delete the following section entirely:
UINib * placeCell = [UINib nibWithNibName:#"Shops" bundle:nil];
//cell = [cellLoader instantiateWithOwner:self options:nil];
NSArray *topLevelItems = [placeCell instantiateWithOwner:self options:nil];
cell = [topLevelItems objectAtIndex:0];
My best guess is that the Nib was being loaded twice and that Xcode was complaining that the data wasn't being loaded by the original. So getting rid of that second entry got my cells loaded and populated with data. Hope this helps someone.

how to delete a subview (button) in table view if segment controller is changed?

the problem that i'm facing is that i have segmented controller in UItable view , selecting index 0 , it suppose to shows buttons( and its working perfectly). the problem is when i select index 1 and the table view changed its contents , the buttons here should all be deleted . if i add these statement, it only delete the button in the last cell
these statement just delete the button of the last cell
[cell willRemoveSubview:downloadButton];
[downloadButton removeFromSuperview];
downloadButton.hidden=YES;
-(void)configureCell: (UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath {
downloadButton = [UIButton buttonWithType:UIButtonTypeCustom] ;
[downloadButton setFrame:CGRectMake(250,8,30,30)];
[downloadButton setTag :indexPath.row];
[downloadButton setImage:[UIImage imageNamed:#"DownloadLesson.png"] forState:UIControlStateNormal];
[downloadButton addTarget:self action:#selector(cellButton:) forControlEvents: UIControlEventTouchUpInside];
if (segOL.selectedSegmentIndex==0) {
[cell addSubview:downloadButton];
} else {
[cell willRemoveSubview:downloadButton];
[downloadButton removeFromSuperview];
downloadButton.hidden=YES;
}
}
- (IBAction)cellButton:(id)sender {
UIButton *play = sender;
NSLog(#"Number of row %d", play.tag]);
}
downloadButton is never added to the view when you call removefromsuperview. It is made anew everytime you use the cell. Buttons keep piling up
you have to save the old button and not make a new one and forgetting the old :)
e.g.:
#interface CellClass : UITableViewCell {
UIButton *downloadButton;
}
...
-(void)configureCell: (UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath {
if(!downloadButton) {
downloadButton = [UIButton buttonWithType:UIButtonTypeCustom] ;
[downloadButton setFrame:CGRectMake(250,8,30,30)];
[downloadButton setTag :indexPath.row];
[downloadButton setImage:[UIImage imageNamed:#"DownloadLesson.png"] forState:UIControlStateNormal];
[downloadButton addTarget:self action:#selector(cellButton:) forControlEvents: UIControlEventTouchUpInside];
}
if (segOL.selectedSegmentIndex==0) {
[cell addSubview:downloadButton];
} else {
[cell willRemoveSubview:downloadButton];
[downloadButton removeFromSuperview];
downloadButton.hidden=YES;
}
}
- (IBAction)cellButton:(id)sender {
UIButton *play = sender;
NSLog(#"Number of row %d", play.tag]);
}

How to get indexPath in a tableview from a button action in a cell?

I have a tableview with two actions in it, for the first on i use the delegate didSelectRowAtIndexPath, for the second one i would like to use an action on a button on my cell to do something else. My problem is that i don't succeed to get the indexpath ? What is the best practice to do that.
If you have added the button to the cell as,
[cell.contentView addSubview:button];
then, you can get the index path as,
- (void)onButtonTapped:(UIButton *)button {
UITableViewCell *cell = (UITableViewCell *)button.superview.superview;
NSIndexPath *indexPath = [tableView indexPathForCell:cell];
// Go ahead
}
I'm using this solution -- getting index path of cell using points
CGPoint center= [sender center];
CGPoint rootViewPoint = [[sender superview] convertPoint:center toView:_tableView1];
NSIndexPath *indexPath = [_tableView1 indexPathForRowAtPoint:rootViewPoint];
NSLog(#"%#",indexPath);
Its work perfectly
here is the full code for a custom button:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
CustomCellFilter *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (!cell) {cell = [[CustomCellFilter alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];}
//configure your cell here
cell.titleLabel.text = #"some title";
//add button
UIButton *myButton = [UIButton buttonWithType:UIButtonTypeCustom];
resetButton.frame = CGRectMake(40, 10, 18, 18);
[resetButton addTarget:self action:#selector(onButtonTapped:) forControlEvents:UIControlEventTouchUpInside];
[resetButton setImage:[UIImage imageNamed:#"someimage.png"] forState:UIControlStateNormal];
[cell.contentView addSubview:myButton];
//afterwards return the cell
return cell;
- (void)onButtonTapped:(UIButton *)button {
UITableViewCell *cell = (UITableViewCell *)button.superview.superview;
NSIndexPath *indexPath = [filterTable indexPathForCell:cell];
NSLog(#"%#", indexPath);
//use indexPath here...
}
In case you are using a collection view, you can use Yogesh method, but change indexPathForRowAtPoint for indexPathForItemAtPoint like this:
CGPoint center= [sender center];
CGPoint rootViewPoint = [[sender superview] convertPoint:center toView:_tableView1];
NSIndexPath *indexPath = [_tableView1 indexPathForRowAtPoint:rootViewPoint];
NSLog(#"%#",indexPath);
In case that your button is moved or Apple changes the view hierarchy for accessory views, this method goes up the chain to look for a UITableViewCell:
- (void)tapAccessoryButton:(UIButton *)sender {
UIView *parentView = sender.superview;
// the loop should take care of any changes in the view heirarchy, whether from
// changes we make or apple makes.
while (![parentView.class isSubclassOfClass:UITableViewCell.class])
parentView = parentView.superview;
if ([parentView.class isSubclassOfClass:UITableViewCell.class]) {
UITableViewCell *cell = (UITableViewCell *) parentView;
NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
[self tableView:self.tableView accessoryButtonTappedForRowWithIndexPath:indexPath];
}
}

How to change it back from activity indicator to disclosure in UITableViewCell after pushViewController

How to change it back from activity indicator to disclosure ??
the problem is:
When i didSelectRow, i made disclosure to activity indicator because it will load some picture for the next view controller and It's worked, but i dont have any luck to change it back to disclosure after pushViewController. i already tried to reload the table from the 2ndViewController but it's crashed.
So anybody have a solution for this problem..
Thanks in advance
This is my code in didSelectRow :
-(void)threadStartAnimating:(id)data
{
[activityView startAnimating];
[self.navigationController pushViewController:bdvController animated:YES];
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
activityView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
[cell setAccessoryView:activityView];
[NSThread detachNewThreadSelector:#selector(threadStartAnimating:) toTarget:self withObject:nil];
// Navigation logic -- create and push a new view controller
if(bdvController == nil)
bdvController = [[DetailViewController alloc] initWithNibName:#"DetailViewController" bundle:[NSBundle mainBundle]];
AppRecord *appRecord = [self.entries objectAtIndex:indexPath.row];
bdvController.appsRecord = appRecord;
bdvController.HeaderText.text = appRecord.appName;
bdvController.title = appRecord.artist;
[bdvController.tableView reloadData];
}
The best way is to set pointer nil to the accessoryView, example:
cell.accessoryView = nil;
cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;
cell.selectionStyle = UITableViewCellSelectionStyleGray;
In my code these operations work fine.

iphone, UITextField in UITableView

I'm trying to add textfields in a tableview. I want a label and a textfield in every row except the last row. I want a switch at the last row.
The problem is that the text fields are overlapping on the rest of the rows. I moved my textfield code inside the if(cell == nil) but that didn't work... here's my code
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *MyIdentifier = #"mainMenuIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:MyIdentifier] autorelease];
[cell setSelectedBackgroundView:[[UIImageView alloc] initWithImage:[UIImage imageNamed:#"highlightstrip.png"]]];
if (tableView.tag == 1) {
UILabel *lblName = [[UILabel alloc] initWithFrame:CGRectMake(10, 10, 90, 20)];
lblName.textAlignment = UITextAlignmentLeft;
lblName.font = [UIFont boldSystemFontOfSize:14];
lblName.backgroundColor = [UIColor clearColor];
lblName.tag = 31;
[cell.contentView addSubview:lblName];
[lblName release];
}
}
if (tableView.tag == 1) {
[(UILabel *) [cell viewWithTag:31] setText:[tableElements objectAtIndex:indexPath.row]];
// check if the last row
if (indexPath.row == 10) {
newsSwtich = [[[UISwitch alloc] initWithFrame:CGRectZero] autorelease];
[newsSwtich addTarget:self action:#selector(switchToggled:) forControlEvents: UIControlEventTouchUpInside];
cell.accessoryView = newsSwtich;
}
else {
UITextField *tempTextField = [[UITextField alloc] initWithFrame:CGRectMake(100, 10, 200, 20)];
tempTextField.delegate = self;
// tempTextField.placeholder = [tableElements objectAtIndex:indexPath.row];
tempTextField.font = [UIFont fontWithName:#"Arial" size:14];
tempTextField.textAlignment = UITextAlignmentLeft;
tempTextField.tag = indexPath.row;
tempTextField.autocorrectionType = UITextAutocorrectionTypeNo; // no auto correction support
tempTextField.keyboardType = UIKeyboardTypeDefault; // type of the keyboard
tempTextField.returnKeyType = UIReturnKeyDone; // type of the return key
tempTextField.clearButtonMode = UITextFieldViewModeWhileEditing; // has a clear 'x' button to the right
[cell.contentView addSubview:tempTextField];
[tempTextField release];
cell.accessoryView = UITableViewCellAccessoryNone;
}
cell.selectionStyle = UITableViewCellSelectionStyleNone;
return cell;
}
When I scroll up and down the text fields are overlapped , I mean after I enter the text in the first row and scroll down, I can see that textfield copied at the last row as well.
Cell reuse is causing the text fields to overlap. Each time the cell is being reused, you are adding a text field or a switch. These are piling up. You will need to remove the older subview which would either be the switch or the text field before adding one.
why you are using two tableviews just do this
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
if(indexPath.row==[tableviewarray count]){
//dont add label or textfield
}
else{
// add label or textfield
}
}

Resources