Inconsistent highlighting on selected UITableViewCell - ios

I have a UITableView in which I need to programmatically select a cell if the data model says that the cell represents the selected choice in a list of items. I do this when I'm configuring the UITableViewCell:
if (group == self.theCase.assignedGroup) {
[self.tableView selectRowAtIndexPath:indexPath animated:YES scrollPosition:UITableViewScrollPositionNone];
self.selectedIndexPath = indexPath;
} else {
[self.tableView deselectRowAtIndexPath:indexPath animated:YES];
}
I am seeing very odd behavior with this. If the first row of the tableview is the one that should be selected, the cell doesn't highlight its background properly. However, if the second row is the one that should be selected, it works as it's supposed to (screenshots at the end).
UPDATE: It could have something to do with the fact that I am loading data for the table asynchronously, and while the data is loading I show a different kind of cell with a progress indicator in that first row. Here's the table view data source code that's handling this:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section
{
if (self.hasMorePages) {
return self.groups.count + 1;
}
return self.groups.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell;
if (indexPath.row < self.groups.count) {
cell = [tableView dequeueReusableCellWithIdentifier:#"DSAssignCell" forIndexPath:indexPath];
[self configureCell:cell forRowAtIndexPath:indexPath];
} else {
cell = [tableView dequeueReusableCellWithIdentifier:#"DSLoadingCell" forIndexPath:indexPath];
[self configureLoadingCell:cell forRowAtIndexPath:indexPath];
}
return cell;
}
- (void)configureCell:(UITableViewCell *)cell
forRowAtIndexPath:(NSIndexPath *)indexPath
{
DSGroup *group = self.groups[indexPath.row];
if (group == self.theCase.assignedGroup) {
[self.tableView selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionNone];
self.selectedIndexPath = indexPath;
} else {
[self.tableView deselectRowAtIndexPath:indexPath animated:YES];
}
cell.textLabel.text = group.name;
cell.tag = kDataCellTag;
}
- (void)configureLoadingCell:(UITableViewCell *)cell
forRowAtIndexPath:(NSIndexPath *)indexPath
{
UIActivityIndicatorView *activityIndicator;
if ([cell viewWithTag:kActivityIndicatorTag]) {
activityIndicator = (UIActivityIndicatorView *)[cell viewWithTag:kActivityIndicatorTag];
} else {
activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
activityIndicator.center = cell.center;
activityIndicator.tag = kActivityIndicatorTag;
[cell.contentView addSubview:activityIndicator];
}
[activityIndicator startAnimating];
cell.tag = kLoadingCellTag;
}
UPDATE As requested, here is the code that handles the asynchronous loading of the group & agent data from the web service:
- (void)viewDidLoad
{
[super viewDidLoad];
[self resetData];
[self loadData];
}
- (void)resetData
{
self.currentPage = 0;
self.hasMorePages = YES;
self.groups = [[NSMutableArray alloc] initWithCapacity:kGroupsPerPage];
self.agents = [[NSMutableArray alloc] initWithCapacity:kAgentsPerPage];
}
- (void)loadData
{
if (self.showingGroups) {
[DSGroup fetchGroupsOnPage:self.currentPage + 1 perPage:kGroupsPerPage success:^(NSArray *groups, NSDictionary *links, NSNumber *totalEntries) {
[self.groups addObjectsFromArray:groups];
[self didLoadDataPage:(links[#"next"] != [NSNull null])];
} failure:^(NSError *error) {
[self showAlert:#"Could not load groups. Please try again later." withError:error];
}];
} else {
[DSUser fetchUsersOnPage:self.currentPage + 1 perPage:kAgentsPerPage success:^(NSArray *users, NSDictionary *links, NSNumber *totalEntries) {
[self.agents addObjectsFromArray:users];
[self didLoadDataPage:(links[#"next"] != [NSNull null])];
} failure:^(NSError *error) {
[self showAlert:#"Could not load users. Please try again later." withError:error];
}];
}
}
- (void)didLoadDataPage:(BOOL)hasMorePages
{
self.hasMorePages = hasMorePages;
self.currentPage++;
[self.tableView reloadData];
}
Here's a screenshot of trying to select (and highlight) the first row, which is wrong (no gray background):
Here's a screenshot of trying to select (and highlight) the second row, which is correct:
Any idea what might be going on here?

I wasn't able to fix this using the built-in selection styles of UITableViewCell, but subclassing it and overriding setSelected:animated fixed it:
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
[super setSelected:selected animated:animated];
self.contentView.backgroundColor = selected ? [UIColor grayColor] : [UIColor whiteColor];
}

Related

Tableview cell expandable with text field causes textfield to jump around

So i have been trying for a little while now to create a table view with expandable sections and one non expandable section. One of the expandable sections should have 3 text fields inside them in which you can edit the test inside the text field. I was able to get that working bt the moment you collapse the section and expand it again the textfield suddenly duplicates itself above and sometimes swaps itself out with the above cell. Ihavent been able to figure out why or how to make it not do that.
The idea is when the user enters text in the field and selects enter the text is stored into an array.
the code:
- (void)viewDidLoad{
[super viewDidLoad];
if (!expandedSections){
expandedSections = [[NSMutableIndexSet alloc] init];
}
manualSensorName = [[NSMutableArray alloc]initWithObjects: #"Sensor",#"",#"2",#"T", nil];
}
- (void)didReceiveMemoryWarning{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Expanding
- (BOOL)tableView:(UITableView *)tableView canCollapseSection:(NSInteger)section{
if (section>0) return YES;
return NO;
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
return 3;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
if ([self tableView:tableView canCollapseSection:section])
{
if ([expandedSections containsIndex:section])
{
return 5; // return rows when expanded
}
return 1; // only top row showing
}
// Return the number of rows in the section.
return 1;
}
- (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];
}
// Configure the cell...
if ([self tableView:tableView canCollapseSection:indexPath.section]){
if (!indexPath.row){
// first row
cell.textLabel.text = #"Expandable"; // only top row showing
if ([expandedSections containsIndex:indexPath.section])
{
UIImageView *arrow = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"arrowUP.png"]];
cell.accessoryView = arrow;
}
else
{
UIImageView *arrow = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"arrowDOWN.png"]];
cell.accessoryView = arrow;
}
}
else {
if (indexPath.row == 1){
NSString *flightNumText = [manualSensorName objectAtIndex:indexPath.row];
cell.textLabel.text = flightNumText;
}
else if (indexPath.row == 2){
txtManualSensor = [[UITextField alloc] initWithFrame:CGRectMake(180, 5, 120, 30)];
txtManualSensor.placeholder = #"Select";
txtManualSensor.delegate = self;
txtManualSensor.autocorrectionType = UITextAutocorrectionTypeNo;
txtManualSensor.backgroundColor = [UIColor whiteColor];
[txtManualSensor setBorderStyle:UITextBorderStyleBezel];
[txtManualSensor setTextAlignment:NSTextAlignmentCenter];
[txtManualSensor setReturnKeyType:UIReturnKeyDone];
// UITextField *playerTextField = [[UITextField alloc] initWithFrame:CGRectMake(180, 5, 120, 30)];
// playerTextField.adjustsFontSizeToFitWidth = YES;
// playerTextField.textColor = [UIColor blackColor];
// playerTextField.placeholder = #"SAMPLE";
// playerTextField.tag = 200;
// playerTextField.delegate = self;
// [cell.contentView addSubview:playerTextField];
cell.textLabel.text = #"Sensor Name";
[cell addSubview:txtManualSensor];
}
else if (indexPath.row == 3){
cell.textLabel.text = #"Some Detail";
cell.accessoryView = nil;
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
}
}
else {
cell.accessoryView = nil;
cell.textLabel.text = #"Normal Cell";
}
return cell;
}
- (BOOL)textFieldShouldEndEditing:(UITextField *)textField {
[manualSensorName replaceObjectAtIndex:2 withObject:textField.text];
return YES;
}
-(BOOL) textFieldShouldReturn:(UITextField *)textField{
[textField resignFirstResponder];
return YES;
}
// Override to support conditional editing of the table view.
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
// Return NO if you do not want the specified item to be editable.
return YES;
}
#pragma mark - Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
if ([self tableView:tableView canCollapseSection:indexPath.section]){
if (!indexPath.row){
[tableView beginUpdates];
// only first row toggles exapand/collapse
[tableView deselectRowAtIndexPath:indexPath animated:YES];
NSInteger section = indexPath.section;
BOOL currentlyExpanded = [expandedSections containsIndex:section];
NSInteger rows;
NSMutableArray *tmpArray = [NSMutableArray array];
if (currentlyExpanded) {
rows = [self tableView:tableView numberOfRowsInSection:section];
[expandedSections removeIndex:section];
}
else {
[expandedSections addIndex:section];
rows = [self tableView:tableView numberOfRowsInSection:section];
}
for (int i=1; i<rows; i++) {
NSIndexPath *tmpIndexPath = [NSIndexPath indexPathForRow:i inSection:section];
[tmpArray addObject:tmpIndexPath];
}
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
if (currentlyExpanded) {
UIImageView *arrow = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"arrowDOWN.png"]];
[tableView deleteRowsAtIndexPaths:tmpArray
withRowAnimation:UITableViewRowAnimationFade];
cell.accessoryView = arrow;
}
else {
UIImageView *arrow = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"arrowUP.png"]];
[tableView insertRowsAtIndexPaths:tmpArray
withRowAnimation:UITableViewRowAnimationFade];
cell.accessoryView = arrow;
}
NSLog(#"tableview row is %ld in section %ld",(long)indexPath.row,(long)indexPath.section);
[tableView endUpdates];
}
[tableView deselectRowAtIndexPath:indexPath animated:YES];
NSLog(#"selected row is %ld in section %ld",(long)indexPath.row,(long)indexPath.section);
if (indexPath.row == 1) {
// update text fields in cell table view
}
}
}
It may be as simple as replacing UITableViewRowAnimationTop by UITableViewRowAnimationFade:
When changing indexes in didSelectRowAtIndexPath, UITableViewCells change physical location (remember that the UITableView is a UIScrollView), and the scroller can't keep track of what your intent is.
UITableViewRowAnimationTop attempts to adjust the scrolling location, but fails.
Other design considerations:
Do not mix the model (the array of data to be displayed) with your view model (the UI displaying the model). In didSelectRowAtIndexPath, you should first re-order your model, then apply it to the cells
Consider not changing indexes on the fly: you may prefer a model that actually reflects the view structure, i.e. a tree.
Have you noticed you are not respecting - (void)tableView:(UITableView *)tableView and sometimes using self tableView:tableView or self.customTableView in the same method? You should use the tableView passed to you.

Custom CheckBoxView for UITableView Duplicating the Check Marks

I have created a CheckBoxView control for my UITableViewCell. The problem I am facing is that once I checkmark one of the top rows and scrolls the same check mark is visible on the bottom rows. This is because of dequeueresuable rows feature and I want to know how can I fix it. Here is the implementation.
CheckBoxView.m:
-(instancetype) initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
[self setup];
[self registerGesturesRecognizers];
return self;
}
-(void) registerGesturesRecognizers {
UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(checkBoxTapped:)];
[self addGestureRecognizer:tapGestureRecognizer];
}
-(void) checkBoxTapped:(UITapGestureRecognizer *) recognizer {
if(self.checkBoxViewSelectionChanged) {
if(!self.isChecked) {
self.checkBoxViewSelectionChanged(self,self.isChecked);
self.isChecked = YES;
}
else {
self.checkBoxViewSelectionChanged(self,self.isChecked);
}
}
}
-(void) check {
[_checkBoxImageView setImage:[UIImage imageNamed:#"small-check"]];
}
-(void) uncheck {
_checkBoxImageView.image = nil;
}
-(void) setup {
self.userInteractionEnabled = YES;
self.layer.borderWidth = 0.5f;
self.layer.borderColor = [UIColor lightGrayColor].CGColor;
_checkBoxImageView = [[UIImageView alloc] initWithFrame:CGRectMake(1, 0, 23, 23)];
[self addSubview:_checkBoxImageView];
}
And here is the cellForRowAtIndexPath method:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
SamplesTableViewCell *cell = (SamplesTableViewCell *) [tableView dequeueReusableCellWithIdentifier:#"SamplesTableViewCell" forIndexPath:indexPath];
Item *sample = [_samples objectAtIndex:[indexPath row]];
cell.productNameLabel.text = sample.product.name;
cell.productColorLabel.text = sample.productColor.name;
[cell.productImageView setImage:sample.productColor.image];
cell.checkboxView.checkBoxViewSelectionChanged = ^(CheckBoxView *checkBoxView, BOOL isChecked) {
if(!isChecked) {
[checkBoxView check];
checkBoxView.isChecked = YES;
}
else {
[checkBoxView uncheck];
checkBoxView.isChecked = NO;
}
};
return cell;
}
The CheckBoxView is actually a UIView on the Storyboard prototype cell whose class is set to CheckBoxView so it is not created dynamically in the cellForRowAtIndexPath event. When I run the above code and checkmark the top rows and scroll then the same checkmark appears on the lower rows.
UPDATE:
Here is my updated code but it still checks and unchecks the rows at the bottom.
cell.checkboxView.checkBoxViewSelectionChanged = ^{
if(!sample.isSelected) {
[sample setSelected:YES];
[cell.checkboxView check];
}
else
{
[sample setSelected:NO];
[cell.checkboxView uncheck];
}
};
Your UIView should not be maintaining the isChecked state for your checkbox. As you pointed out, due to cell reuse all the others will then be checked because the same UIView is being used.
Your model objects powering your data need to maintain the state, or your controller.
For example:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
SamplesTableViewCell *cell = (SamplesTableViewCell *) [tableView dequeueReusableCellWithIdentifier:#"SamplesTableViewCell" forIndexPath:indexPath];
// Some code...
if ([self.myDatasource[indexPath.row] isChecked]) {
cell.checkBoxView.isChecked = YES;
}
return cell;
}
Just some rough pseudocode, but hopefully you get the idea. Potential ways to implement this state maintained is either in the form of model objects, or in your UIViewController have an NSArray containing an NSDictionary representing each row in your UITableView, where each key-value pair maintains a particular state for your UITableViewCell.

UIPickerView in UITableView

First of all: I know that some people already posted topics like that, but nobody gave a clear answer in all those topics.
I have a settings-UITableView in my application. Now i want to add a TableViewCell where i can choose between some numbers with a UIPickerView.
In the tableView didSelectRowAtIndexPath method i created the if statement for the right cell
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
if (indexPath.row == 1){
}
}
How can i add a UIPickerView for this cell? It should slide up from the bottom and it would be nice if there is something like a "done" button.
Have you seen the sample program released with iOS 7 called DateCell? It uses a UIDatePicker, but I found it pretty straightforward to adapt it to a regular UIPickerView.
It does exactly what you're describing: edit a UITableViewCell with a picker that opens directly under the row you're editing.
This is also based on your other question, so I am including those functionalities here as well (i.e. the switch).
In your YourTableViewController.h
set:
#interface YourTableViewViewController : UITableViewController <UIPickerViewDataSource, UIPickerViewDelegate>
In your YourTableViewController.m
Paste this code:
#interface YourTableViewViewController ()
#property NSInteger toggle;
#property (strong, nonatomic) UIPickerView *pickerView;
#end
#implementation YourTableViewViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.toggle = 0;
self.pickerView = [[UIPickerView alloc] initWithFrame:(CGRect){{0, 0}, 320, 480}];
self.pickerView.delegate = self;
self.pickerView.dataSource = self;
self.pickerView.center = (CGPoint){160, 640};
self.pickerView.hidden = YES;
[self.view addSubview:self.pickerView];
}
#pragma mark - Table view data source
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return 2;
}
- (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.selectionStyle = UITableViewCellSelectionStyleNone;
}
if(indexPath.row == 0)
{
[cell.contentView addSubview:self.mySwitch];
}
if(indexPath.row == 1)
{
cell.textLabel.textColor = [UIColor darkGrayColor];
if(self.toggle == 0)
{
cell.textLabel.text = #"Choose a number";
}
else
{
cell.textLabel.text = #"Cancel";
}
}
// Configure the cell...
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if(indexPath.row == 1)
{
if(self.toggle == 0)
{
self.toggle = 1;
[self.tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:YES];
[self bringUpPickerViewWithRow:indexPath];
}
else
{
self.toggle = 0;
[self.tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:YES];
[self hidePickerView];
}
}
}
- (void)bringUpPickerViewWithRow:(NSIndexPath*)indexPath
{
UITableViewCell *currentCellSelected = [self.tableView cellForRowAtIndexPath:indexPath];
[UIView animateWithDuration:1.0f
delay:0.0f
options:UIViewAnimationOptionCurveEaseInOut
animations:^
{
self.pickerView.hidden = NO;
self.pickerView.center = (CGPoint){currentCellSelected.frame.size.width/2, self.tableView.frame.origin.y + currentCellSelected.frame.size.height*4};
}
completion:nil];
}
- (void)hidePickerView
{
[UIView animateWithDuration:1.0f
delay:0.0f
options:UIViewAnimationOptionCurveEaseInOut
animations:^
{
self.pickerView.center = (CGPoint){160, 800};
}
completion:^(BOOL finished)
{
self.pickerView.hidden = YES;
}];
}
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component
{
self.toggle = 0;
[self.tableView reloadData];
[self hidePickerView];
NSLog(#"row selected:%ld", (long)row);
}
- (NSString*) pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component
{
return [NSString stringWithFormat:#"%d", row+1];
}
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView
{
return 1;
}
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component
{
return 10;
}
#end
Tested.
Addendum:
Also, in your storyboard, change the separator for YourTableView to None (looks better in your case here).
Table View Separator:
in viewDidLoad, add:
self.tableView.separatorStyle = UITableViewCellSeparatorStyleSingleLine;
And in bringUpPickerViewWithRow
after:
UITableViewCell *currentCellSelected = [self.tableView cellForRowAtIndexPath:indexPath];
add:
self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
[self.tableView setNeedsDisplay];
And lastly, in hidePickerView, add:
self.tableView.separatorStyle = UITableViewCellSeparatorStyleSingleLine;
[self.tableView setNeedsDisplay];
Create custom ViewController that include UIPickerView and Done
button.
Add it as subview on your tableview. (Place it bottom so
that it does not visible).
Once user click appropriate cell, you
can animate picker-view from bottom.
Once user select an item
animate and position it to bottom.
I have done above steps for same purpose. If you want I can send custom controller that include UIPickerView and done button.

Swipe to delete row not Working

I'm implementing in my PFQueryTableViewController ( Use Parse.com ) the works Swipe to Delete . When I go to perform the action of cancellation of the cell gives me this error and the app crashes.
Unigo 10/08/2013 15:40:50.683 [ 29142 : a0b ] User Online : between
Unigo 10/08/2013 15:40:54.495 [ 29142 : a0b ] * Assertion failure in
- [ UITableView _endCellAnimationsWithContext : ] , / SourceCache/UIKit_Sim/UIKit-2903.2/UITableView.m 1330 Unigo 10/08/2013
15:40:54.499 [ 29142 : a0b ] * Terminating app two to uncaught
exception ' NSInternalInconsistencyException ' , reason : 'Invalid
update : invalid number of rows in section 0 . The number of rows
contained in an existing section after the update ( 5 ) must be equal
to the number of rows contained in That section before the update ( 5
) , plus or minus the number of rows inserted or deleted from That
section (0 inserted , 1 deleted ) and plus or minus the number of rows
moved into or out of That section (0 moved in , 0 moved out). '
Can you help me figure out where I'm wrong ?
#implementation FFEsami
#synthesize FFNewLabelEsameInAttesa,FFViewEsameInAttesaAvviso,clock,ShowPanel,ArrayMutable;
-(id)initWithCoder:(NSCoder *)FFaDecoder {
self = [super initWithCoder:FFaDecoder];
if (self) {
self.parseClassName = FF_ESAMI_CLASS;
self.pullToRefreshEnabled = YES;
}
return self;
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
[self SetGraphSectionView];
[self queryForTable];
self.tableView.allowsSelectionDuringEditing = YES;
self.tableView.allowsMultipleSelectionDuringEditing = YES;
// Uncomment the following line to preserve selection between presentations.
// self.clearsSelectionOnViewWillAppear = NO;
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
self.navigationItem.leftBarButtonItem = self.editButtonItem;
[self.navigationItem.leftBarButtonItem setTintColor:[UIColor whiteColor]];
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent];
[self loadObjects];
}
#pragma mark - Parse
- (void)objectsDidLoad:(NSError *)error {
[super objectsDidLoad:error];
// This method is called every time objects are loaded from Parse via the PFQuery
}
- (void)objectsWillLoad {
[super objectsWillLoad];
// This method is called before a PFQuery is fired to get more objects
}
- (PFQuery *)queryForTable {
PFQuery *FFQueryPerTableView = [PFQuery queryWithClassName:FF_ESAMI_CLASS ];
[FFQueryPerTableView whereKey: FF_ESAMI_USER equalTo: [PFUser currentUser]];
[FFQueryPerTableView orderByDescending:FF_ESAMI_DATA_STRING];
if ([self.objects count] == 0) {
}
return FFQueryPerTableView;
}
-(void)SetGraphSectionView {
FFViewEsameInAttesaAvviso =[[UIView alloc]initWithFrame:CGRectMake(0, 0, 320, 50)] ;
FFNewLabelEsameInAttesa = [[UILabel alloc]initWithFrame:CGRectMake(60, -1, 200, 50)];
FFNewLabelEsameInAttesa.numberOfLines = 2;
FFNewLabelEsameInAttesa.textAlignment = NSTextAlignmentLeft;
FFNewLabelEsameInAttesa.textAlignment = NSTextAlignmentLeft;
FFNewLabelEsameInAttesa.lineBreakMode = NSLineBreakByTruncatingTail;
clock = [[UIImageView alloc] initWithFrame:CGRectMake(17, 11, 25, 25)];
// ShowPanel = [[UIImageView alloc] initWithFrame:CGRectMake(280, 27, 20, 20)];
}
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
return 50.0;
}
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
if(section==0){
PFQuery *query = [PFQuery queryWithClassName:FF_ESAMI_CLASS];
[query whereKey:FF_ESAMI_USER equalTo:[PFUser currentUser]];
[query whereKey:FF_ESAMI_STATUS equalTo:[NSNumber numberWithBool:YES]];
[query orderByAscending:FF_ESAMI_DATA_STRING];
[query getFirstObjectInBackgroundWithBlock:^(PFObject *object, NSError *error) {
if (!object) {
NSString *titolo = [NSString stringWithFormat:#"Nessun esame imminente"];
FFNewLabelEsameInAttesa.text = titolo;
FFNewLabelEsameInAttesa.font = [UIFont fontWithName:#"HelveticaNeue" size:13];
FFNewLabelEsameInAttesa.textColor = [UIColor grayColor];
FFNewLabelEsameInAttesa.numberOfLines = 2;
FFViewEsameInAttesaAvviso.backgroundColor = [UIColor clearColor];
clock.image= [UIImage imageNamed:#"FFIMG_InAttesa"];
} else {
NSString *titolo = [NSString stringWithFormat:#"%#", [object objectForKey:FF_ESAMI_TITOLO]];
FFNewLabelEsameInAttesa.text = titolo;
FFNewLabelEsameInAttesa.font = [UIFont fontWithName:#"HelveticaNeue" size:13];
FFNewLabelEsameInAttesa.textColor = [UIColor whiteColor];
FFViewEsameInAttesaAvviso.backgroundColor = [UIColor colorWithRed:(203/255.0) green:(162/255.0) blue:(86/255.0) alpha:(0.90)];
clock.image= [UIImage imageNamed:#"FFIMG_InAttesaBianco"]; }
}];
ShowPanel.image= [UIImage imageNamed:#"FFDownView"];
[FFViewEsameInAttesaAvviso addSubview:FFNewLabelEsameInAttesa];
[FFViewEsameInAttesaAvviso addSubview:clock];
[FFViewEsameInAttesaAvviso addSubview:ShowPanel];
}
return FFViewEsameInAttesaAvviso;
}
-(NSInteger) numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 0;
}
- (FFCustomListaEsamiCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath object:(PFObject *)object {
FFCustomListaEsamiCell *cell = (FFCustomListaEsamiCell * )[self.tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
if (!cell) {
cell = [[FFCustomListaEsamiCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"Cell"];
}
NSString *text = [object objectForKey:FF_ESAMI_TITOLO];
cell.FFTitoloEsameLabel.numberOfLines = 2;
cell.FFTitoloEsameLabel.text = text;
NSDateFormatter *FFDataFormattata = [[NSDateFormatter alloc] init];
[FFDataFormattata setDateFormat:FF_DATE_FORMATTER];
cell.FFDataEsameLabel.text = [NSString stringWithFormat: #"%#",[FFDataFormattata stringFromDate:[object objectForKey:FF_ESAMI_DATA_STRING]]];
PFObject *rowObject = [self.objects objectAtIndex:indexPath.row];
if([[rowObject objectForKey:FF_ESAMI_STATUS] boolValue])
{
//DATO CONVALIDATO
cell.last.image = [UIImage imageNamed:#"FFIMG_InAttesa"];
/*UIImage *btn = [UIImage imageNamed:#"FF_ChechOk"];
[cell.FFInserisciConvalida setImage:btn forState:UIControlStateNormal];
[cell.FFInserisciConvalida addTarget:self
action:#selector(FFConvalidaDato)
forControlEvents:UIControlEventTouchUpInside];
[cell.FFAltreAzioni addTarget:self
action:#selector(ActionSheetForStateInAttesa)
forControlEvents:UIControlEventTouchUpInside];*/
} else {
//DATO NON CONVALIDATO
//UIImage *btn = [UIImage imageNamed:#"FFIMG_ClockTAG"];
/* [cell.FFInserisciConvalida setImage:btn forState:UIControlStateNormal];
[cell.FFInserisciConvalida addTarget:self
action:#selector(FFMettiInAttesa)
forControlEvents:UIControlEventTouchUpInside];
[cell.FFAltreAzioni addTarget:self
action:#selector(ActionSheetForStateConvalidato)
forControlEvents:UIControlEventTouchUpInside];*/
}
return cell;
}
/*
// Override if you need to change the ordering of objects in the table.
- (PFObject *)objectAtIndex:(NSIndexPath *)indexPath {
return [objects objectAtIndex:indexPath.row];
}
*/
/*
// Override to customize the look of the cell that allows the user to load the next page of objects.
// The default implementation is a UITableViewCellStyleDefault cell with simple labels.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForNextPageAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"NextPage";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.textLabel.text = #"Load more...";
return cell;
}
*/
#pragma mark - Table view data source
// Override to support conditional editing of the table view.
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
// Return NO if you do not want the specified item to be editable.
return YES;
}
// Override to support editing the table view.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete) {
PFObject *object = [self.objects objectAtIndex:indexPath.row];
[object deleteInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
[self loadObjects];
}];[tableView reloadRowsAtIndexPaths:[tableView indexPathsForVisibleRows] withRowAnimation:UITableViewRowAnimationFade];
// [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
else if (editingStyle == UITableViewCellEditingStyleInsert) {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
}
// Override to support rearranging the table view.
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath
{
}
// Override to support conditional rearranging of the table view.
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath
{
// Return NO if you do not want the item to be re-orderable.
return YES;
}
#pragma mark - Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[super tableView:tableView didSelectRowAtIndexPath:indexPath];
}
#end
You have this code:
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 0;
}
which is telling the table to have 0 cells in it after the update when the table is expecting 4. If you are using static cells in the storyboard then you should not implement the numberOfRowsInSection function.
If you remove that code then you should have no problems.
EDIT:
Or if you do want the numberOfRowsInSection function then you need to hook up a datasource to the tableview. This would allow you to return the count of the datasource. Then, when you delete a row you would remove the corresponding object in your datasource so that the count is 1 less than it previously was.

values change when scrolling my tableview

my tableview changes values when I scroll up or down. It seems to use the same values from other parts of the table. I imagine its an error in my cell creation, here is the code.
Please tell me what I did wrong, thank you!
EDITED ADDED WHOLE CODE
//global indexpath to remember which cell tapped
NSIndexPath *globalPath;
#interface SearchViewController ()
#end
#implementation SearchViewController
//Load implementation once per launch
- (void)viewDidLoad
{
[super viewDidLoad];
[self linkInputTableToDelegate];
_temporaryResultsArray =[[NSMutableArray alloc]init];
_flurryArray=[[NSMutableArray alloc]init];
_numberOfSections=6;
}
-(void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:NO];
[InputTable reloadData];
textFromUserDefaults=[[[HelperMethods alloc]init]getObjectUserDefault:#"textFiltered"];
[self addTextToFlurryArrayForFlurryAndSavedLists:_textFromUserDefaults];
}
-(void)viewDidDisappear:(BOOL)animated{
}
- (IBAction)searchButtonPressed:(UIButton *)sender {
self.tabBarController.selectedIndex = 1;
}
//Makes the input table respond to delegate table view methods
-(void)linkInputTableToDelegate{
_inputTable.dataSource=self;
_inputTable.delegate=self;
}
-(void)performSearch:(NSString*)text{
//do search
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
int numberOfRows=_numberOfSections;
//Rows for iPhone 4
if ([[UIScreen mainScreen]bounds].size.height==480) {
numberOfRows=numberOfRows;
//Rows for iPhone 5
}else if ([[UIScreen mainScreen]bounds].size.height==568){
numberOfRows=numberOfRows+1;
}
return numberOfRows;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
//In reality groups are created with 1 row inside, this is to allow spacing between the rows
return 1;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *kCellID = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kCellID];
if (!cell) {
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:kCellID];
}
//Is the cell the same as the one clicked when going to ingredient filter
BOOL cellIndexPathSameAsSelected=[self isCellIndexSameAsPreviousClicked:indexPath];
cell.textLabel.textColor=[UIColor blackColor];
if (cellIndexPathSameAsSelected && _textFromUserDefaults!=nil) {
if (![cell.textLabel.text isEqualToString:_textFromUserDefaults]) {
cell.textLabel.text=_textFromUserDefaults;
[self performTextSearch:_textFromUserDefaults];
}
}
return cell;
}
//Compares the previous clicked cell with the cell now selected
-(BOOL)isCellIndexSameAsPreviousClicked: (NSIndexPath*)cellPath{
if (cellPath.row == globalPath.row && globalPath.section==cellPath.section) {
return YES;
}
else{
return NO;
}
}
- (void)updateTableViewWithExtraRow :(NSIndexPath*)rowSelected{
NSLog(#"number of sections =%i",_numberOfSections);
if (rowSelected.section == _numberOfSections) {
_numberOfSections ++;
}
}
#pragma mark - Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *cellText = [tableView cellForRowAtIndexPath:indexPath].textLabel.text;
[[[HelperMethods alloc]init]saveObjectToUserDefaults:cellText :#"textFiltered"];
globalPath = indexPath;
[self updateTableViewWithExtraRow:indexPath];
}
-(void)addTextToFlurryArrayForFlurryAndSavedLists:(NSString*)text{
if ([_flurryArray count]==0 &&[text length]>0) {
[_flurryArray addObject:text];
}
for (int i=0;i<[_flurryArray count];i++) {
NSString *textInArray=[_flurryArray objectAtIndex:i];
if (![textInArray isEqualToString:text]) {
[_flurryArray addObject:text];
}
}
NSLog(#"Total number of saved items = %i",[_flurryArray count]);
}
// Dispose of any resources that can be recreated.
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
The problem is you are only changing the texts in the cells, when those conditions are met. For example, when your cellIndexPathSameAsSelected is NO, you leave the cell unmodified. So you should add an else and do some settings there too.
EDIT:
if (cellIndexPathSameAsSelected && _textFromUserDefaults!=nil) {
if (![cell.textLabel.text isEqualToString:_textFromUserDefaults]) {
cell.textLabel.text=_textFromUserDefaults;
[self performTextSearch:_textFromUserDefaults];
}
} else {
cell.textLabel.text = [NSString string];
}
After your [tableView dequeueReusableCellWithIdentifier:kCellID]; call, you have to check if your cell can actually be reusable or not with the followings statements :
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:...];
}
You should be doing something like this
static NSString *kCellID = #"Cell";
// Acquire the cell if possible.
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kCellID];
if (cell == nil) // Not possible to re-use cell, so create a new cell
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:kCellID];
}

Resources