I have a ViewController that has a TableView generated with Custom Cells. Each Custom Cell has a NSDictionary that generates few arrays.
I want to find the way to replace one array in Custom Cell using [myArray replaceObjectAtIndex:... withObject:...] pointing towards a specific cell in a TableView, provided that I already know the indexPath of that cell that contains that array.
Another words I have to somehow indicate that Custom Cell indexPath, get there and refresh array.
Is there a way to accomplish that mission in Objective-C?
You can get a cell reference using cellForRowAtIndexPath call if you know the indexPath of the cell that you want to change if each of your custom cell is different unique.
But I strongly recommend changing the data model by getting it using cell location as the reference. Because table views reuse cell you could end up with unexpected results. You should instead change your table model and then reload the table or perhaps reload just the specific cells that have changed.
Once you have the indexPath of your cell, you can do something like:
[self.tableView beginUpdates];
[self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObjects:indexPathOfYourCell, nil] withRowAnimation:UITableViewRowAnimationNone];
[self.tableView endUpdates];
You got several option for reloading the UITableView taken inside the UIViewController
say your table view is such
UITableView *tableView
case 1: Reload the whole table view appledoc
[tableView reloadData];
case 2: Reload all the rows inside a section appledoc
[tableView reloadRowsAtIndexPaths:yourIndexPath withRowAnimation: UITableViewRowAnimationNone];
case 3: Reload multiple section at once appledoc
tableView reloadSections:yourIndexPathSet withRowAnimation:UITableViewRowAnimationNone];
NB :: must do any of these operation in your mainThread / gui thread.
Solution.
First, we need to make sure that every Custom Cell has a #property NSInteger cellIndex;. When tableView populates Custom Cells, make sure to add cell.cellIndex = indexPath.row;. That way every Custom Cell will have it equal to tableView row.
Second, when making changes in parent viewController, in the end of update we post notification [[NSNotificationCenter defaultCenter] postNotificationName:#"refreshCollectionView" object:self];.
Third, in Custom Cell we listen to that notification:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(refreshCollect) name:#"refreshCollectionView" object:nil];. That triggers method (also included in Custom Cell) with reference to cellIndex:
-(void) refreshCollect {
if (cellIndex == [[[NSUserDefaults standardUserDefaults] valueForKey:#"cellIndex"] intValue]) {
NSURL *newsImgURL = [NSURL URLWithString:[NSString stringWithFormat:#"http://www.website.com/core/functions/ios/page.news.image?news_id=%#", [[NSUserDefaults standardUserDefaults] valueForKey:#"newsID"]]];
NSData *newsImgData = [NSURLConnection sendSynchronousRequest:[NSURLRequest requestWithURL:newsImgURL] returningResponse:nil error:nil];
newsImgDict = [NSJSONSerialization JSONObjectWithData:newsImgData options: NSJSONReadingMutableContainers error:nil];
newsImgID2 = [NSMutableArray arrayWithArray: [newsImgDict valueForKey:#"image_id"]];
newsImgTime = [NSMutableArray arrayWithArray: [newsImgDict valueForKey:#"timestamp"]];
newsIDImg = [NSMutableArray arrayWithArray: [newsImgDict valueForKey:#"news_id"]];
newsImgDescr = [NSMutableArray arrayWithArray: [newsImgDict valueForKey:#"description"]];
newsImgExt2 = [NSMutableArray arrayWithArray: [newsImgDict valueForKey:#"ext"]];
} }
In my case I have NSDictionary that rebuilds some arrays just for that particular Custom Cell.
Related
In my cellForRowAtIndexPath and I pre selecting a row:
NSArray *tempArray = [[communityPrefs objectForKey:#"Community"] componentsSeparatedByString:#","];
NSMutableArray *tempArrayMutable = [[NSMutableArray alloc] initWithArray:tempArray];
if ([tempArrayMutable containsObject:cell.textLabel.text])
{
[selectedAreaTable selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionNone];
}
This part works great. However the selected item is at the bottom of the page and the user will not see it, but when they scroll down to that cell it is selected.
Now I am trying to write a piece of code the will deselect all selected cells like so:
for(NSIndexPath *index in selectedAreaTable.indexPathsForSelectedRows)
{
if(index.row != 0)
{
[selectedAreaTable deselectRowAtIndexPath:index animated:NO];
}
}
but after I run this code the cell at the bottom is still selected. So my question is, why is this cell not being deselected? Is it because its not there until you scroll to it? How can I fix this problem?
Is it because its not there until you scroll to it?
Yes. I believe that you should change "selected" state using cell objects only for visible rows. All other rows should retrieve "selected" status in cellForRowAtIndexPath method
user979331,
you dont have to remove the selection in seperate method rather you can handle that too in cellForRowAtIndexPath as well.
You can declare the array NSMutableArray *tempArrayMutable as a property,
when you want to deselect all cell lets assume a method named clear all selection
-(void)clearAllSelection {
[self.tempArrayMutable removeAllObjects];
self.tableView reloadData];
}
Finally in cellForRowAtIndexPath change as
if ([self.tempArrayMutable containsObject:cell.textLabel.text])
{
[selectedAreaTable selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionNone];
}
else{
[selectedAreaTable deselectRowAtIndexPath:indexPath animated:NO];
}
In my iOS app I am using Core Data to show data on a table view this is working fine .
what i want to do is :
when clicking on the navigation back button of view controller i want
to delete tableview cell if the textview on the View controller is empty .
i want to do it like Apple note app .
can any one hellp me with sample code please
here is my code to check if textview is empty or not
if( self.info .text.length <= 0 ){
}
1.Make sure your DataSource is something like this a
NSMutableArray *dataSource = [NSMutableArray alloc]initWitObjects:#"One",#"Two",#"Three",#"Four",#"Five",nil];
2.Remove wanted objects from your dataSource like so
[dataSource removeObjectAtIndex:2];
Delete rows in UITableView with animation
Create indexPaths to delete like this
NSArray *deleteIndexPaths = [[NSArray alloc] initWithObjects:
[NSIndexPath indexPathForRow:2 inSection:0],
nil];
Finally delete them from the tableView like this
[self.tableView beginUpdates];
[self.tableView deleteRowsAtIndexPaths:deleteIndexPaths withRowAnimation:UITableViewRowAnimationFade];
[self.tableView endUpdates];
Or you can simply call
[self.tableView reloadData];
I have a UITableView that is populated with cells by core data and an NSFetchedResultsController. I have a custom button on my custom cells, which I'm planning on using to delete the cell. It's very easy to add the standard swipe-to-delete, but I'd rather use this custom button. Does anyone know how I could hook up an action to the button that would delete the entry from the data model and delete the cell from the UITableView? I cannot find a good solution to this for the life of me.
EDIT:
This is the code I have to delete it using standard swipe-to-delete. Any way I could modify it to work with a button?
if (editingStyle == UITableViewCellEditingStyleDelete) {
[self.tableView beginUpdates];
// Delete the task
Task *taskToDelete = [self.fetchedResultsController objectAtIndexPath:indexPath];
NSLog(#"Deleting (%#)", taskToDelete.name);
[self.managedObjectContext deleteObject:taskToDelete];
[self.managedObjectContext save:nil];
// Delete the row
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
[self performFetch];
[self.tableView endUpdates];
}
General overview:
Connect the button to an action on your controller, deleteRow:(id)sender;
The sender will be the button. Get its superview, then the superview of its superview, and so on until you have a reference to the UITableViewCell. (Search in a loop using isKindOfClass:, don't assume the button is only 1, 2, 3 levels down.)
Call your table view's indexPathForCell: method to convert the cell reference to an index path.
Use objectAtIndexPath: on the fetched results controller to get the object.
Then delete it! If you are handling the NSFetchedResultsController delegate methods they will take care of removing the deleted row.
Sample code (typed without a compiler to check it):
- (void)deleteRow:(id)sender
{
id view = [sender superview];
while (view && ![view isKindOfClass:[UITableViewCell class]]) {
view = [view superview];
}
UITableViewCell *cell = view;
NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
NSManagedObject *task = [fetchedResultsController objectAtIndexPath:indexPath];
[self.managedObjectContext deleteObject:task];
[self.managedObjectContext save:nil];
}
That's it. DO NOT try to remove the row from the table view. When the object is deleted, NSFetchedResultsController will detect the change and update your table view, assuming you set up the delegate methods as described in the documentation for NSFetchedResultsController. If you haven't, read the documentation (just option-double-click on "NSFetchedResultsController" in Xcode).
I am having some trouble with UITableView's reloadData method. I have found that it only calls cellForRowAtIndexPath if there are new cells being added or taken away.
Example: I have five cells, each containing five strings. If I add a new cell and call reloadData, the table is updated and I see it. Yet if I go into one of the five cells, add a new string, then return and call reloadData, none of the table view's delegate methods is called.
My question: Is it possible to force the table view to completely reload the data in all of its visible cells?
I found the problem- I had my cell customization code in the if(cell == nil) block, so because the cells were being recycled, they weren't being changed. Taking my customization code out of that block fixed the problem.
I've found that reloading sections reloads data more readily. If you just have one section you can try:
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationNone];
You can try this
[tableView reloadData];
or
[tableView deleteRowsAtIndexPaths:[NSMutableArray arrayWithObjects:indexPath, nil] withRowAnimation:UITableViewRowAnimationFade];
put this in commitEditingStyle method.
Well, the table view's only going to call cellForRowAtIndexPath on the visible cells but if you do call reloadData it will do that so there's something else going on here and I'm not sure what it is.
Even if your tableview has 50 rows, there only will exist as much cells as can be visible at one time. That's the whole story behind the reuseIdentifier. So forcing 'all the cells' doesn't exist. If a new cell appears, the data is loaded dynamically.
The only way to change a cell is to change the data that is delivered by the dataSource method cellForRowAtIndexPath.
Do not take your code out of the if (cell == nil) block. Instead, create a representative identifier for the cell you're making; try and make sure that all of the cell's content is referred to in the identifier. For example, if you have 3 numbers showing, make sure to have those three numbers in the identifier in a unique way that would only refer to a cell that has such content.
Let's say you have three NSArray properties in your class, array1, array2, and array3 that have int values wrapped inside of NSNumber objects. You want to use those NSArrays to fill a UITableView, this is what I'd do:
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *identifier = [NSString stringWithFormat:#"%#-%#-%#",
[[array1 objectAtIndex:indexPath.row] intValue],
[[array2 objectAtIndex:indexPath.row] intValue],
[[array3 objectAtIndex:indexPath.row] intValue]];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc]
initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:identifier] autorelease];
//Build your cell here.
}
return cell;
}
I initialize data in my table with an array in viewDidLoad and then add the data to the cell. This a standard way of doing it that I read in a book.
This is what I have:
- (void)viewDidLoad {
[super viewDidLoad];
//Create array and add data ("tableViewValues" is initialized in .h file)
tableViewValues = [[NSMutableArray alloc]init];
[tableViewValues addObject:#"$280,000.00"];
[tableViewValues addObject:#"$279,318.79"];
}
// 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:CellIdentifier] autorelease];
}
NSString *cellValue = [tableViewValues objectAtIndex:indexPath.row];
cell.textLabel.text = cellValue;
return cell;
}
So when the view loads, those two currency values are in my table.
Now in another function, I populate a another array with different currency numbers depending on what the user enter in a textfield. How would I update my current table view and replace those values with the values in my other array? Can anyone help me? Thanks!
You can call
[self.tableView reloadData];
to reload all data, however, you will need to program a way to have the array that you want populate the table. Maybe you want your -cellForRowAtIndexPath to call a private method that conditionally picks the correct array.
You have to remove all values from your array then you have to call table reload data
// In the method where you will get new values
[tableViewValues removeAllObjects];
[tableViewValues add:#"new values"];
//reload table view with new values
[self.tableView reloadData];
I've had this problem many times and I always make the same mistake.
[self._tableview reloadData] works!
The question is the place where you populate the table.
I did it in -(void)viewDidLoad and the table was updated with the same data. Apparently nothing happens.
Update the content of your table view (plist, dictionary, array, whatever) in the same place where you call [self._tableview reloadData], just before.
THAT IS THE KEY!!!
Do a test:
#pragma mark - Actions
- (IBAction)refreshParams:(id)sender {
self.dictionary = nil;
[self._tableView reloadData];
}
You can see how disappear the tableview content when you push the refresh button. Obviously, this is an example. I use a dictionary to populate the table and a button to refresh the content.
You'd need to (release and) recreate same tableViewValues array and then call reloadData method on tableView like this:
[self.tableView reloadData];
This will work if you're on table view controller and self.tableView points to the table in question.
in that function, after assigning values to the new array, all you have to do is
[tableViewValues removeAllObjects];
tableViewValues = [[NSMutableArray alloc] arrayByAddingObjectsFromArray:newArray];
[self.tableView reloadData];
[self.tableView reloadData];
will help you for this.
In Swift,
tableView.reloadData() or self.tableView.reloadData(), where tableView is a property in your ViewController.