UITableView reloads incorrectly after removeObjectAtIndex and reloadData - objective-c

I have a UITableView which has data from an NSMutableArray.
When calling removeObjectAtIndex on the array and then reloadData on the table, the cell is removed fine, but then scrolling shows the table hasn't reloaded properly.
The code I am using is:
[_squadArray removeObjectAtIndex:myIndex;
[[self tableView] reloadData];
This is what the table looks like afterwards:
FYI, the table should be full and not stop where it stops.
Update: cellForRowAtIndexPath method
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *MyIdentifier;
MyIdentifier = #"cell";
ClubSquadPlayerTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
PFObject *object = [_squadArray objectAtIndex:indexPath.row];
cell.playerNameLabel.text = [NSString stringWithFormat:#"%# %#", [object valueForKey:#"first_name"], [object valueForKey:#"last_name"]];
cell.squadNumberLabel.text = [[object valueForKey:#"squad_number"] stringValue];
cell.positionLabel.text = [object valueForKey:#"position"];
if ([[object valueForKey:#"fitness_status"] isEqualToString:#"Fit"])
{
cell.fitnessStatusImageView.image = [UIImage imageNamed:#"fit"];
} else if ([[object valueForKey:#"fitness_status"] isEqualToString:#"Doubtful"])
{
cell.fitnessStatusImageView.image = [UIImage imageNamed:#"doubtful"];
} else if ([[object valueForKey:#"fitness_status"] isEqualToString:#"Injured"])
{
cell.fitnessStatusImageView.image = [UIImage imageNamed:#"injured"];
}
return cell;
}

It seems to me like your problems can be solved by Parse's own UI.
The Parse UI version of a UITableViewController is a PFQueryTableViewController. You can still use your custom cells and outlets, but the controller is a bit different.
In this case your issue will be solved by the [yourTable loadObjects] function which allows you to update your table from the PFQuery.
You set up the PFQueryTableViewController directly from a PFQuery without arrays. See more here.

When you try to remove the object as i saw it have santax error
Your code is
[_squadArray removeObjectAtIndex:myIndex;
[[self tableView] reloadData];
But it should be
[_squadArray removeObjectAtIndex:myIndex];
[[self tableView] reloadData];
There were ] missing

Related

Issue with UITableView on scroll

I'm experiencing an issue with my UITableView which fetches data from a data table on parse.com. The issue is that every time I scroll down, hiding the first cell completely and then scroll back up, the text on the first cell's titleL is that of another cell. Kindly look at my code and let me know what I'm doing wrong. Also are there any better practices for my code when working with UITableViews in the future?
Code
- (void)viewDidLoad {
[super viewDidLoad];
[self someMethod];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *CellIdentifer = [NSString stringWithFormat:#"CellIdentifier%i",num];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifer];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifer];
}
UILabel *titleL = [[UILabel alloc] initWithFrame:CGRectMake(10,10,300,20)];
titleL.text = myTitle;
[cell addSubview:titleL];
return cell;
}
-(void) someMethod {
for (int i = 0; i < arr.count; i++) {
PFQuery *query = [PFQuery queryWithClassName:#"SomeClass"];
[query whereKey:#"objectId" equalTo:[arr objectAtIndex:i]];
[query getFirstObjectInBackgroundWithBlock:^(PFObject *object, NSError *error) {
if (!object) {
} else {
myTitle = [object objectForKey:#"title"];
num = i;
[feed beginUpdates];
[feed reloadRowsAtIndexPaths:myArr withRowAnimation:UITableViewRowAnimationAutomatic];
[feed endUpdates];
}
}];
}
}
You need to write your your tableView:cellForRowAtIndexPath: in such a way that it doesn't matter in which order it is called.
That method is called whenever the UITableView needs to get a cell (sometimes this doesn't mean that it's displayed). It will get called multiple times and you cannot rely on a specific order (for obvious reasons: you cannot predict how the user will scroll).
Now, your problem is that your implementation uses myTitle to assign a title. But that value is not calculated inside tableView:cellForRowAtIndexPath:. You need to change your code in such a way that you always can access the required value for your index path, no matter in which order or how often that method is called.
For example, in someMethod you can store your values from [object objectForKey:#"title"] in an NSMutableArray or in a NSMutableDictionary (with #(i) as key). Then you can query the title for each index path in tableView:cellForRowAtIndexPath:.

Using UIButton to refresh the UItableViewCell content

There are two components, UItableView and a UIButton, in my app.
The UItableViewcell will load the data from remote database fulfilled by JSON.
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *TableIdentifier = #"tableidentifier"
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:TableIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:TableIdentifier] autorelease];
}
NSDictionary *voc_list=[listData objectAtIndex:indexPath.row];
NSLog(#"%#",voc_list);
cell.textLabel.text = [[(NSDictionary*)voc_list objectForKey:#"vocabulary_list"]objectForKey:#"Vocabulary"];
cell.detailTextLabel.text=[[(NSDictionary*)voc_list objectForKey:#"vocabulary_list"]objectForKey:#"Translation"];
cell.textLabel.font = [UIFont boldSystemFontOfSize:15];
return cell; }
However, I want to refresh all the table content when user press the button, and I try to implement the following code:
-(IBAction)historyPressed:(id)sender{
isToogle = !isToogle;
if(isToogle){
// Back to original table content
}else{
// Following codes will communicate with remote server and filter data to the app.
// The app go smooth here.
NSError *error = NULL;
NSDictionary *getStuID=[NSDictionary dictionaryWithObjectsAndKeys:student_id,#"Stu_ID", nil];
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:getStuID options:NSJSONWritingPrettyPrinted error:&error];
[self sendTOcompareByJSON:jsonData];
//Following codes are trying to show/refresh the data on tableview, but the app will go crash.
CGPoint location = [sender locationInView:self.table];
NSIndexPath *indexPath = [self.table indexPathForRowAtPoint:location];
UITableViewCell *new_cell=[self.table cellForRowAtIndexPath:indexPath];
historyList_= [NSArray arrayWithArray:personalized_history];
NSDictionary *dic = [historyList_ objectAtIndex:indexPath.row];
new_cell.textLabel.text=[[(NSDictionary*)dic objectForKey:#"history_list"]objectForKey:#"Vocabulary"];
new_cell.detailTextLabel.text=[[(NSDictionary*)dic objectForKey:#"history_lsit"]objectForKey:#"Score"];
}
}
In the historypressed method just try to call [yourtableview reloaddata].. After your setting of all the cell content do reloaddata,it will refresh the tableview.
I dont know how you getting data from [self sendTOcompareByJSON:jsonData];. If its a sync call, to webserver, then you can just update your datasource (in ur case you are filling tableview using listData) just after this.So once the listData is updated with the new contents, then you should reload your tableview like this [self.table reloadData]
If its an async call to web server, then do update the datasource and reload the table on callback.
Hope this helps.

Why is my app crashing during Table View Insert Row?

My app is only crashing in a specific way. Here's a break down of what's going on.
I can type in text in a UITextView and tap a button that saves the text and adds a row to a UITableView in another UIViewController. I can then tap on the desired cell from the UITableView and that UIViewController will dismiss and the text will appear again on the main UIViewController.
I have another button that simply clears out the UITextView so I can type in new text.
If I view the text from an added row and then tap the "Add" button to input new text and then tap the "Save" button my app crashes.
Here's some of the code:
didSelectRow Code:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
//Setting the text stored in an array into a NSString here
_displayString = [_savedNoteArray objectAtIndex:indexPath.row];
[self dismissViewControllerAnimated:YES completion:nil];
}
save button code:
- (IBAction)saveNote
{
if (_noteView.aTextView.text == nil)
{
[_noteArray addObject:#""];
Note * tempNote = [[Note alloc] init];
_note = tempNote;
[_savedNotesViewController.savedNoteArray addObject:tempNote];
NSIndexPath * tempNotePath = [NSIndexPath indexPathForRow: [_savedNotesViewController.savedNoteArray count]-1 inSection:0];
NSArray * tempNotePaths = [NSArray arrayWithObject:tempNotePath];
[_savedNotesViewController.noteTableView insertRowsAtIndexPaths:tempNotePaths withRowAnimation:NO];
[[NSNotificationCenter defaultCenter] postNotificationName:#"AddNote" object:nil];
}
else
{
[_noteArray addObject:self.noteView.aTextView.text];
Note * tempNote = [[Note alloc] init];
_note = tempNote;
[_savedNotesViewController.savedNoteArray addObject:tempNote];
NSIndexPath * tempNotePath = [NSIndexPath indexPathForRow:[_savedNotesViewController.savedNoteArray count]-1 inSection:0];
NSArray * tempNotePaths = [NSArray arrayWithObject:tempNotePath];
//**** This is where the app is crashing *****
[_savedNotesViewController.noteTableView insertRowsAtIndexPaths:tempNotePaths withRowAnimation:NO];
[[NSNotificationCenter defaultCenter] postNotificationName:#"AddNote" object:nil];
}
Note * myNote = [Note sharedNote];
myNote.noteOutputArray = _noteArray;
}
add butt code (makes a new UITextView):
- (IBAction)addButtonTapped
{
[[NSNotificationCenter defaultCenter] postNotificationName:#"AddNote" object:nil];
}
in my viewWillAppear to show the selected row text I do this:
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
self.noteView.aTextView.text = _savedNotesViewController.displayString;
}
note code (singleton class):
static Note * sharedNote = nil;
- (id)initWithNote:(NSString *)newNote
{
self = [super init];
if (nil != self)
{
self.note = newNote;
}
return self;
}
+ (Note *) sharedNote
{
#synchronized(self)
{
if (sharedNote == nil)
{
sharedNote = [[self alloc] init];
}
}
return sharedNote;
}
When the app crashes I get this:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[Note isEqualToString:]: unrecognized selector sent to instance 0x8875f20'
Stepping through my code, the text is being added to the array, but when it comes time to insertRowsAtIndexPaths the app blows up.
Any advice is much appreciated!
* EDIT // TableView Code **
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [_savedNoteArray 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.selectionStyle = UITableViewCellSelectionStyleNone;
}
//I have a feeling this could be where an issue is.
NSString * cellString = [_savedNoteArray objectAtIndex:indexPath.row];
cell.textLabel.text = cellString;
return cell;
}
One potential issue (which may not be your crash, but will cause issues regardless) is that you are storing Note objects in the savedNoteArray BUT you are trying to use them as strings (your code below):
//Setting the text stored in an array into a NSString here
_displayString = [_savedNoteArray objectAtIndex:indexPath.row];
Then you assign that displayString to a UITextView's text property (which is supposed to be an NSString*):
self.noteView.aTextView.text = _savedNotesViewController.displayString;
The short form of this issue can be summarized as...
Note *note = [[NSNote alloc] init];
[array addObject:note];
textView.text = array[0];
This will clearly cause issues. You're basically assigning a 'Note' object to something that is supposed to be a string.
This probably leads into the crash that you're experiencing in the cellForRowAtIndexPath: method of your table view data source. Are you using Note objects there as well, or are you properly assigning NSStrings to views?

why UITableView is showing data when I touch it?

I have a UITableView loading some tweets, all works nicely but when I populate the table it doesn't enter to the CellForRow method til I touch the table view.... does somebody know how to solve this?
tab = [[UITableView alloc] initWithFrame:CGRectMake(10, 300, 400, 200) style:UITableViewStylePlain];
tab.dataSource = self;
tab.delegate = self;
then...
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"entra a cellula");
Tweet *t = [tweets objectAtIndex:indexPath.row];
static NSString *MyIdentifier = #"MyIdentifier";
// Try to retrieve from the table view a now-unused cell with the given identifier.
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
// If no cell is available, create a new one using the given identifier.
if (cell == nil) {
// Use the default cell style.
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:MyIdentifier];
}
cell.textLabel.text = t.screenName;
cell.detailTextLabel.text = t.text;
cell.imageView.image = t.profileImage;
return cell;
}
Best guess based on what we know is that you aren't adding the table as a subview on the main thread. This means that redrawing doesn't occur until you scroll. Try wrapping the addsubview call in a GCD call like so:
dispatch_async(dispatch_get_main_queue(), ^() {
[self addSubview:tab];
});
When you finished loading your tweets call this method:
[tab reloadData];
to reload your tableView data source.

How Do I add a UITableViewCell using a button in IB

So what I am trying to do is create a notepad style addition to my app.
All I want is for it to work exactly like apples existing notepad where you click the "add" button in the top right, then it creates a new note that you can write in and then when you click done it adds the note to a Cell in a UITableView.
I already have the UITableView and everything set up I just need to know how to run this action
-(IBAction)noteAdd:(id)sender{
}
And then when you click that button it does what I described above.
How would I go about doing this? I'm a little lost.
This Is How I am Adding the TableView to the scene, just By the way.
//tableview datasource delegate methods
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
return 1;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return cameraArray.count;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
CustomCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell"];
if(cell == nil){
cell = [[CustomCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"Cell"];
}
NSEnumerator *enumerator = [cameraArray objectEnumerator];
id anObject;
NSString *cellName = nil;
while (anObject = [enumerator nextObject]) {
cellName = anObject;
}
//static NSString *cellName = [cameraArray.objectAtIndex];
cell.textLabel.text = [NSString stringWithFormat:cellName];
return cell;
}
In UITableView
- (void)insertRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation
So you'd do something like
-(IBAction) noteAdd:(id)sender
{
NSIndexPath *newCellPath = [NSIndexPath indexPathForRow:cameraArray.count
inSection:0];
// I'm assuming cameraArray is declared mutable.
[cameraArray addObject:#"New item"];
[self.tableView insertRowsAtIndexPaths:#[newCellPath]
withRowAnimation:UITableViewRowAnimationFade];
}
While I'm at it, a few comments on your code:
I'm pretty sure this code:
NSEnumerator *enumerator = [cameraArray objectEnumerator];
id anObject;
NSString *cellName = nil;
while (anObject = [enumerator nextObject]) {
cellName = anObject;
}
is a rather roundabout way of getting the last string in the array. You could do that easier with cameraArray.lastObject. But I don't think that's what you want either, I think you're looking for
// XCode >= 4.5:
cellName = cameraArray[indexPath.row];
// XCode < 4.5:
cellName = [cameraArray objectAtIndex:indexPath.row];
And the next line:
cell.textLabel.text = [NSString stringWithFormat:cellName];
Best case, this creates an extraneous string. If the cell name happens to have a % in it, you'll almost certainly either get an error or an EXC_BAD_ACCESS. To fix that error you could use
cell.textLabel.text = [NSString stringWithFormat:#"%#", cellName];
but there's really no reason to. Just assign the string directly:
cell.textLabel.text = cellName;
Or if you insist on a copy:
cell.textLabel.text = [NSString stringWithString:cellName];
// OR
cell.textLabel.text = [[cellName copy] autorelease];
// OR