I have a UITableView backed with an NSFetchedResultsController.
When the controller goes off screen I follow suggestion to resign as fetched controller delegate.
Therefore I totally rely on context notification and reloadData.
Basically, I analyze the notification and set a flag needsReload=YES when in the inserted/updated/deleted object there's a relevant entity and property for the table.
On viewWillAppear I check for this variable and call reloadData.
However the NSFetchedResultsController has an sort ordering on NSManagedObject attribute 'pname'.
When in other view controller I update pname say from 0name to Zname, I can intercept the context did change notification and the table is correctly reloaded at next viewWillAppear.
The problem is that I would expect the cell to goes at bottom because of the sort ordering, but instead it remains updated at top, I suppose because reloadData only updates visible cell. How can I solve this ?
The fetched results controller does not track changes in the managed object context anymore if you set its delegate to nil.
Therefore, after re-assigning a delegate, you have to call
[self.fetchedResultsController performFetch:&error]
first and then
[self.tableView reloadData];
Related
I have a custom UITableViewCell that extends UITableViewCell and implements MFMailComposeViewControllerDelegate. (i.e. UITableViewCell<MFMailComposeViewControllerDelegate>).
When a button is clicked in the custom table cell, I present a mailController with presentModalViewController. The user can then type the email or cancel, everything works.
But when didReceiveMemoryWarning is called while the mailController is present, and then when the mailController closes, the app crashes. I get this error:
"-[CustomCell respondsToSelector:]: message sent to deallocated instance 0xf4988b0"
Now I'm pretty sure its because the table view that owns the tablecell has been deallocated hence the cell has been deallocated but does anyone have any suggestions to go about fixing this? I mean I guess I could switch the MailCompose delegate to the table cell's table view's view controller but I'd rather not. I'd rather keep it in the table cell. Any ideas?
You should not be using a table view cell as the MFMailComposeViewControllerDelegate. Because of the nature of cells and their reuse, it's hard to pin a particular instance to be a delegate, especially a delegate of a view controller on top of the view controller the cell is being shown, and on this particular case, the memory warning might be causing a flush of the cache of cells.
The MFMailComposeViewControllerDelegate should be the delegate of the table view where the cell is. It's also a better MVC pattern.
I'm working in Objective C. I have a UITableViewController with about 25 cells that push to a UIViewController. When the user hits back, I want to see if the user entered the correct data for the given cell. (I have a working bool , we'll call it isCellComplete for now). If isCellComplete is true, I want to add a checkmark as the accessory to the cell. I've been trying to put the test in cellForRowAtIndexPath but that method does not seem to run and refresh the cells everytime the view appears. Anyone have suggestions?
You should look into the UITableView method reloadRowsAtIndexPaths:withRowAnimation:. This is much more elegant than reloading the whole table view. And if you don't want an animation, you can specify UITableViewRowAnimationNone and it will look just like reloadData but be much more efficient.
You should do the check in tableView:cellForRowAtIndexPath: and if the check passes, set the cell's accessory to UITableViewCellAccessoryCheckmark. Then when you tell the table view to reload the appropriate row(s), it'll automatically call tableView:cellForRowAtIndexPath: on the data source and update that cell.
You could just call
[self.tableView reloadData];
in
-(void) viewWillAppear:(BOOL)animated
And that will make cellForRowAtIndexPath be called again when the view appears
When I call performSegueWithIdentifier: I am overriding prepareForSegue: afterwards in order to set some properties on my destination view controller. However, I'm trying to understand the order of operations here to make sure it's safe.
I instantiate the destination view controller with:
MyViewController *myVC = (MyViewController*)segue.destinationViewController;
Afterwards I set a few properties on it - namely, I initialize the creation of another object which handles loading different web views, and then set properties on that object:
[myVC setFormHandler:
[[WebFormHandler alloc] initWithSelectedFormName:[self theFormName]]];
In viewDidLoad: of my destination controller, I then query the properties on this object, and use this to load the correct web view. Now, I am slightly confused by which happens first - the setting of the properties, or viewDidLoad: on the destination controller. Can it be said for certain that the properties of the view controller will always be set from prepareForSegue: before the viewDidLoad: is called?
It looks like you may have found your answer from that other post, but I just wanted to add one point of clarification.
The standard flow is
performSegue -> prepare -> loadView (in destination controller)
However, in popover segues, the destination view is loaded prior to the prepareForSegue call.
NOTE: This is no longer the case in iOS 8. In iOS 8, popover segue's views are NOT loaded by the time prepareForSegue is called.
How can I reload a UITableView without noticing the user behind the iDevice?
I'm updating my UITableViews datasource in -applicationWillEnterForeground: by sending a notification to my RootViewController that observes the notification and runs a selector that calls [self.table reloadData] but I want this update to be made in stealth and not showed to the user.
It seems that -viewWillAppear: is fired before -applicationWillEnterForeground:
I'm a little confused by your question, but I think what you're looking for is a way to reload the data before it appears on screen, yes?
I'd say figure out a way to call your data refresh method directly, not via a notification. The notification de-couples the data refresh from -applicationWillEnterForeground, giving the view time to appear.
Sequence is probably something like this:
applicationWillEnterForeground
viewWillAppear
The view appears.
Notification observer method is called
a) Notification observer method calls [self.table reloadData]
Table view refreshes on-screen.
What you want is to couple your notification method to a method that gets called before the view appears. Call it directly from applicationWillEnterForeground or viewWillAppear.
That way it'll play out like this:
applicationWillEnterForeground
a) your data refresh method is called. (no need to call [self.table reloadData], the tableView hasn't loaded the data at all yet.)
viewWillAppear
The view appears.
Table view is loaded with already refreshed data.
or like this:
applicationWillEnterForeground
viewWillAppear
a) your data refresh method is called. (no need to call [self.table reloadData], the tableView hasn't loaded the data at all yet.)
The view appears.
Table view is loaded with already refreshed data.
I was wondering if it's possible to remove empty cells (empty = cells with no textLabel) after all the cells are created in a UITableView.
Why do you have empty cells? Are you using a consistent technique to control both the number of cells in your table and the content of those cells?
If you're using a UITableViewController, then your controller is automatically declared as the tableview's datasource. If you're using a UIViewController, then you'll declare it as comforming to the UITableViewDataSource protocol (and connect it up in Interface Builder).
Either way, as the tableview's datasource, your controller is required to implement two methods:
– tableView:cellForRowAtIndexPath:
– tableView:numberOfRowsInSection:
Presumably you're providing the data for the tableview with an array or other means inside -tableview:cellForRowAtIndexPath:. Inside this method the cell's label will be set from an entry in your array. And inside tableView:numberOfRowsInSection: you'll be doing something like [myArray count] to return the number of cells. tableview:cellForRowAtIndexPath will be called as many times as you tell it to (dictated by what you provide in tableview:numberOfRowsInSection:). If the datasource array changes, and you'd like to reflect the changes in your tableview, then you can call
[self.tableview reloadData]; //if inside a UITableViewController
[self.myTableViewOutlet reloadData]; //if inside a UIViewController
Note that reloadData reloads the entire tableview, so in some cases this may be computationally expensive. In this case, instead of calling reloadData you can focus on individual rows with the method: deleteRowsAtIndexPaths:withRowAnimation: (see UITableView Class Reference)
As the app delegate, you are responsible for providing cells. It is your responsibility to return every cell, and the number of cells in the table. Therefore as the app delegate you should have a means (be it by NSMutableArray or otherwise) to mutate the data that you return to the table view.