UITableView Full of CoreData Results Plus Additional Cell? - iphone

I have a UITableView which I fill with CoreData fetched results. I want to add an single custom cell to the top of my UITableView and have managed to do so, however it requires me to have to call this:
return [sectionInfo numberOfObjects] + 1;
Then this:
NSIndexPath *path = [NSIndexPath indexPathForRow:(indexPath.row - 1) inSection:indexPath.section];
The second line has to be called in almost all of the UITableView Delegate methods to account for the additional cell at indexPath 0. However it causing a ton of problems which I won't list as every time i make changes here and there whatever it may be this is the root of my problem.
Is there any other method you would suggest to simply add my single custom cell to the top of my CoreData tableview?
Any help much appreciated.

If you are using this extra cell as a header, consider setting the tableView.tableHeaderView to a custom view.

Related

UITableView renames every eighth cell

I have a UITableView in my MainViewController. When a user taps a cell,
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
selectedRow = indexPath;
....
[self performSegueWithIdentifier:#"OtherViewControllerSegue" sender:self];
}
and they are taken to another UIViewController (let's call it OtherViewController). In OtherViewController, the name for the selected cell is set. When OtherViewController is dismissed, it updates the cell in MainViewController with the new name:
[[[mainvc.myTableView cellForRowAtIndexPath:mainvc.selectedRow] textLabel] setText:namecell.textField.text];
[self.navigationController popViewControllerAnimated:YES];
This all works fine until I have more cells than will fit on the screen. If there are more cells than will fit on the screen (8 for iPhone or 16 for iPad), then this will also set the name for every eighth or sixteenth cell respectively. Any ideas on what I am doing wrong?
Update:
- (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [pointsTableView dequeueReusableCellWithIdentifier:#"myTableCell"];
return cell;
}
This is due to cell-reuse and you are mixing up your model with your view (in the MVC context).
A table-cell is a transient thing, once it goes off the screen it is reused (instead of creating new cells) when another cell is needed. This is what the dequeueReusableCellWithIdentifier: method does.
This means you can't store data in there and expect it to still be valid later on. In this example you are trying to store the name in the table cell. The reason to set a property (like the label text) on any view object is purely for display, not for storage. So to solve this problem you should maintain a list of objects in your model (this could be in separate classes or in an array in your mainvc object for example). Then in cellForRowAtIndexPath: you should set the label text every time - even when there should be no label you need to set it to nil or an empty string because the cells are re-used it might contain something from the last time it was used.
Update:
Instead of calling cellForRowAtIndexPath: yourself and setting its text, you should set the text in your model using a method or property in your controller and then tell the table view to reload that cell. The code might look something like this:
// This code is in where you want to set the text from
[mainvc setText:someText forIndexPath:indexPath];
.. and in your main view controller:
- (void)setText(NSString*)newText forIndexPath:(NSIndexPath*)indexPath
{
// Store the text in your model here...
...
// If the view is loaded, the table view should reload the cell.
if(self.isViewLoaded)
{
[self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
}
The table view will then call cellForRowAtIndexPath: where the text will be set correctly. This may seem a little convoluted at first, but when you get used to using the Model-View-Controller design pattern you will find that keeping the jobs of each MVC component separate like this will mean your code is tidier, easier to understand, has less bugs, is easier to update/extend, etc.
You're trying to store data (the new name) in a view (the cell's label). What's probably happening is that when you re-use cells in the data source's cellForRowAtIndexPath method, some of them are ones that have had this text set for them and it's still there.
The better idea is to make your changes in whatever array you use as cell information and then reload the table view to make the changes visible.
As I suppose, you shouldn't call cellForRowAtIndexPath by yourself. It can be called to create cell, not to change it.
You can update your table by passing needed string to the first view via delegate, for example. And on the event (user sets the name) you can update all table and set needed names to cells.
Hard to say exactly what the problem is, but one possible solution might be this:
Make sure that in your cellForRowAtIndexPath you are initializing the cells like this:
// Create the Cell
static NSString *recordCell = #"pickerTableCell";
cell = [tableView dequeueReusableCellWithIdentifier:recordCell];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:recordCell];
}
I know this is primarily a memory solution, but might gelp here too.
Also, look through your code and check how you are determining which cell is renamed. You could be accidentally calling the rename on more than one cell without realizing it

Delete Row from UITableView Outside TableView Delegate

I am trying to delete a row from UITableview outside the delegate method. I am calling a method when I click a button inside a table cell and trying to delete the row inside that method.
Here is the function I am using
UIButton *btn = (UIButton*)sender;
int tag = btn.tag;
UITableViewCell *buttonCell = (UITableViewCell*)[[btn superview] superview];
NSIndexPath *indexPath = [self.msgTbl indexPathForCell:buttonCell];
[deleg.rmessages removeObjectAtIndex:buttonRow];
[self.msgTbl deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationTop];NSInteger buttonRow = indexPath.row;
[self.msgTbl reloadData];
Using this one or two rows get deleted but after that its crashing giving exception
Number of rows before and after deletion must be same
How can I do this in ios?
Thanks
Your problem is that the data that is being taken to populate your table isn't consistent with the table after deleting the cell. Make sure your dataSource methods provide the correct data after doing this (for example, if it is an array of objects you are using to populate the table, you must remove the object from the array as well)
The root issue is that this method:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section;
Must return the correct number of rows, and it isn't. When you remove the item from deleg.rmessages, is this the same object that is being used to supply the return value of the above method? (Something like [deleg.rmessages count]?)
Also, in my experience that exception often gives you more details, in particular:
How many items it had before
How many were added/deleted
How many it expects to have vs. how many it does have after the reload
Do you see anything like this being mentioned? If so, it would be worth including in your question.
Sidenote:
It's a bad idea to rely on:
UITableViewCell *buttonCell = (UITableViewCell*)[[btn superview] superview];
To return the UITableViewCell. You appear to assign the tag of the button to a local variable, but never use it. (Maybe this would be a good place to store the index of the UITableViewCell, and then subclass the cell to maintain an ivar to the button?) This is only part of the problem.

How to add data in UITableview at run time?

I want to add data or cells in UITableview when we scrolled to the last cell of the UITableView. In other words, as we have seen in many iPhone apps, that when we reached to the last cell of the UITableview the more cells get added to it at runtime. I want to achieve that functionality in my project.
Means Add some more cells to the UITableview when we reached to the previously added last cell.
Please tell me the sample code or any tutorial for it.
I think that there are 2 questions here.
First, how to detect the end of tableVeiw? Second, how to add cells dynamically?
First question, I think it can be done by observing the value of content offset of scrollView or current indexPath of tableView cell. The content offset of scrollView can be observed by following method of UIScrollViewDelegate. The content offset is a property of scrollView.
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
CGFloat currentOffset = scrollView.contentOffset;
// Detect scrolling to bottom by this offset value.
....
}
The index of cell may be decided by the method of UITableViewDataSource.
- (UITableViewCell *)cellForRowAtIndexPath:(NSIndexPath *)indexPath
Second question can be overwhelmed by the methods of UITableView.
beginUpdates
endUpdates
insertRowsAtIndexPaths:withRowAnimation:
deleteRowsAtIndexPaths:withRowAnimation:
insertSections:withRowAnimation:
deleteSections:withRowAnimation:
I remember the sample codes in this official document will teach how to use above methods. They don't reload all cells, but careless operations will result in crash easily.
If you have data stored in an array, then you would definitely have the count of how many cells you want.
Use the below code for your help
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{ return [array count];}
i believe this would have helped you.
Populate the tableView by using an array (as you should always do). When the delegate method cellForRowAtIndexPath ask your for the last cell, repopulate your array with new (more) data and make a [tableView reloadData].
you just want to add data in table?
Then, first you may need to add one more view, modal view. You should write some specification about what you want to add in this view.
Next you send a message to existing data array(Assume you use array)
[ExistingArrayData addObject:TemporalilyNewObject];
And you can sort the data by using NSSortDescriptor.
NSSortDescriptor *nameSorter = [[NSSortDescriptor alloc] initWithKey:KEY_VALUE ascending:YES selector:#selector(caseInsensitiveCompare:)];
[ExistingArrayData sortUsingDescriptors:[NSArray arrayWithObject:nameSorter]];
In this case, this method sorts the data by KEY_VALUE, ascending.
Finally, you shoule add this code in RootView's viewWillAppear method.
[self.tableView reloadData];
This code notifies app of change of data.
-(void) tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath{
int i=0;
i=indexPath.row;
int count=[urTablearray count];
if (count == (++i)) {
//do ur stuff here
here add object to urTablearray
[urTablearray addObject:#"vijay"];
}
}
Note: urTablearray is NSMutableArray type not NSArray

iOS SDK: How to get a cell in a table view that is not visible?

How do I get the cell for an indexPath which is not currently visible in the table? (cell is out of range)
Code for getting my cell:
NSString *name = [[(ELCTextfieldCell *)[tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]] rightTextField] text];
-cellForRowAtIndexPath... returns nil because the cell at the required indexPath is out of range. So how do I get the correct cell and not nil?
The UITableView only keeps the visible cells. If you need one that isn't visible you have to call the tableView:cellForRowAtIndexPath: of the UITableView dataSource. So, if self is a class that is the dataSource:
UITableViewCell * cell = [self tableView:table cellForRowAtIndexPath:indexPath];
This is not how it works. You need to grab and store the information as soon as it is entered or changed. It may easily get out of scope and you cannot guarantee your cell lives long enough. Well, technically you can hold onto it (and always return the very same cell for the same index path), but I'd question that design.
UITableViewCells are made to be reused/recycled in a way that the users won't need to create more cells than the number of visible ones.
You usually don't need to access a cell that is not visible. It should be enough you access your datasource and get/set the correspondent data there. Cells are for showing some state of the datasource. Not the datasource itself :)
Edit:
You said you need some information (text) from one cell above, right? If you use cellForRowAtIndexPath: method the cell will be recreated but you might not get the text that was in the textfield. The reason? because probably you didn't save it somewhere else. If you did save it, then access that directly instead of going through the cell.
In continue to #Raphael's answer, Here is the (working) swift 3 solution:
UITableViewCell cell = self.tableView(self.tableView, cellForRowAt: indexPath)
Is the information to fill your table coming from an array? Could you not pull that directly out of the array at 0 index, same as your cellForRowAtIndexPath would presumably retrieve and fill that cell when it is displayed?

Reloading UITableViewCell on select

Okidoke. Here's my problem: I have a series of complex UITableViewCells set up to display stories from a news feed (yawn). What I want to happen, is for the cell background image and height to change on selection (as a means of marking that the story has been read).
Now, prior to setting up dequeueing, I was able to do this with a simple [self.tableView reloadData]. That seems to be a no-go with dequeued cells; reloading the table view does not redraw the cells to match their changed state.
I've tried reloadRowsAtIndex- and while this works - beautifully - for the first cell a user clicks on, it goes wonky after that point: sometimes the cell reloads correctly, sometimes not.
Obviously, each story is an NSMutableDictionary object. I'm using an NSNumber object to track whether or not a story has been read.
I would post the code, and I will if anyone asks, but I'm looking for a generic solution that could be implemented in any UITableViewController (share the love).
So, simply put: how does one reliably redraw complex cells on selection?
Try giving each cell a unique ID in order for dequeuing to work, your cells should be coming back with their changed states if you use a unique id for each cell, so 20 cells = 20 ids, hope this helps
Assuming you have the index path, you can access the cell and manipulate it directly:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// handle the selection...
UITableViewCell *cell = [tableView cellForRowAtIndexPath: indexPath];
if (nil != cell) {
//
// now update the cell to reflect the new state
//
}
}