iOS: Delete all view controllers on stack except active one - ios

In one view controller the user is able to restore the coredata database. After this restore a lot of stuff probably doesn't work anymore in the other view controllers already in memory even though I've rebuilt the stack. So I just want to get rid all of them, so that always the viewDidLoad methods gets triggered again if reopening again one of these view controllers.
How can I force that?

I'd say you should put the load data code of each view controller in another function than viewDidLoad, for example reloadData. All of your view controllers then also need to be subscribed to a notification (e.g. reloadAllViewControllers). Then using [NSNotificationCenter defaultCenter] postNotificationName:#"reloadAllViewControllers" you can call all reloadData functions to reload all your viewControllers.

Related

How to make the views refresh between detail & master view controllers of UISplitViewController?

I have a split view controller in which either side has table views and needs table data reloading every time some interaction happens on either side. I have implemented delegate to update my detail view controller whenever a cell is selected on left side (master) of split view controller.
1.I wish to know do I need to implement another delegate to make it happen both ways (i.e. updating master view when a )or is there any generic approach.
2.I have already written code for both classes, so what is happening is that when i select a cell on left , right updates via a delegate method reloading/refreshing the view BUT the methods like viewWillAppear/viewDidAppear/viewWillDisapper ...etc are not called. I am now manually calling viewWillAppear method from the delegate Method that's triggered on left cell selection. Is there a technique i am missing so that the class methods are called automatically. or Can anyone point to the best approach to use a splitViewController?
First, it may not be a good idea to call viewWillAppear, etc from your code because those behaviors could change in the future. (e.g., viewDidLoad used to be called multiple times in the early iOS versions, now it is called once per instance). You could just move your code into a separate method.
Second, you may want to look at NSNotification as a way to communicate the changes. It's easy and doesn't require you to keep any delegate pointers around.
For example you might add to the child view controller .h:
#define MASTER_UPDATED #"MasterUpdated"
#define DETAIL_UPDATED #"DetailUpdated"
and then in the master controller something like:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(updateFromDetail:) name:DETAIL_UPDATED object:nil];
...
-(void)updateFromDetail:(NSNotification *)no
{
NSDictionary *nd = [no userInfo]; // get relevant information
// take action....
}
From the detail side, when an item is tapped, you'd send a message like:
NSDictionary *userInfo = #{#"somekey":#"somevalue", #"anotherkey":#"anothervalue"};
[[NSNotificationCenter defaultCenter] postNotificationName:DETAIL_UPDATED
object:self userInfo:userInfo];
You probably need to use one of the reload... methods of UITableView to reload the table. The reloadData method will reload the complete table. If you know which rows are changed, then it is probably better to use one of the other methods.
If the changes are also in the number of row, then you'll need to use one of the insert... or delete... methods to get proper animations.
See the UITableView documentation for all the details.
For your first question, I would expect the detail view controller to have the master as a delegate. But the master should simply know which detail view controller is on the right. After all, he has started it.
Hi #Divjyot I am now working on similar scenario but I have to change to a different viewcontrolelr in detailVIewController(SecondaryViewController) on clicking a cell, so this is what I did
I created and array with all viewcontrollers in masterViewController(PrimaryViewCOntroller) and passed to the detailViewController on cell selection using a delegate. So on clicking the cell in primaryViewControlelr updates the secondaryViewCOntroller with a new ViewController. If u want more info abt how to implement this comment below
Asnwering to your questions:
1º The protocol UISplitViewControllerDelegate has a method which said when splitviewcontroller is going to change the display mode. This method is very useful to update data of some view controller.
- (void)splitViewController:(UISplitViewController *)svc willChangeToDisplayMode:(UISplitViewControllerDisplayMode)displayMode;
2º For other hand, you can need to update the data of view controller when user selects a cell or any other actions.
If you share more information about your code I can be more specific.

Is dealloc always called? Even when you swipe close an app?

I wonder if dealloc is always called when you close a ViewController in Objective-C for iOS. I've done some tests and it seems like it. Except I don't get any logs when I swipe close the application. I figured that maybe XCode doesn't log things if you fully close the app like that.
The reason I wonder this is because I'm sending analytic data when one of the ViewControllers closes and I kinda need to know if dealloc is always called or if there's any better way doing this.
If you want to get notified when app is killed via Swiping , you can use applicationWillTerminate method and in this method , you can check for current view controller in navigation stack and send analytical data for that View controller .
It's not defined when dealloc will be called for a view controller (or pretty much any class), therefore don't use it for sending analytics.
Use viewDidDisappear instead, which you can rely on to be called when the view controller has been removed.

Completely Unload UIViewController in ARC iOS App

I'm using the Facebook iOS SDK to have users authenticate and when they log out and log back in, the state of the main view controller, the one with the logout button, is in the same state as when they left it.
How can I completely remove the view controller from memory so that every time they log back in, the view controller's viewDidLoad method is called and the view controller is re-initialized.
When done with it each time you need to release all references.
When you need to show it again, create and use a new instance. This is the only way to ensure viewDidLoad is called each time you need it.
Or if you want to keep reusing the same one over and over, add a restart method (or something similar). The implementation of this method can reset the UI to whatever starting state you need. Or you can put this logic in the viewWillAppear: method. This depends on whether viewWillAppear: can be called due to pushing and popping other view controllers.

UIViewController - View events life cycle and registering for KVO / Notifications

I'm wondering if there's any way -viewWillAppear: would be called without a matching -viewDidAppear:. Ditto for -viewWillDisappear and -viewDidDisappear.
The root of my question is where to register and unregister for KVO and / or NSNotifications of an object who's change notifications will cause the view controller to update views.
For example, I have a model object that is being processed asynchronously and it's string properties could change. I'd like the view controller to KVO those properties and have any changes reflected by swapping out the text of a label managed by said view controller.
Where do you register and unregister for notifications and why?
EDIT:
A gotcha I've come across is what to do in cases of application state change (e.g. -applicationWillResignActive, -...didEnterBackground, etc). These changes don't seem to trigger view controller lifecycle methods. Any best practices here?
With the standard container view controllers, you will always get will/did messages in pairs. If you have written your own view controller container, you may not, but that would be a bug in the container implementation.
Most of the time, you'll want to set things up and tear things down in the 'will' messages. This gives your view controller the earliest shot at anything it needs to do before it becomes "active" and also shuts down things as early as possible when you no longer need them.
When pushing a view controller in nav stack, it is entirely possible that a notification will occur during the push animation. If you set up the observers in viewDidAppear, you would miss that notification. You want to be listening as soon as possible.
Likewise, I would reckon that viewDidDisappear is too late to remove callbacks. For example, a location manager could be stopped in viewDidDisappear, but another location update could be delivered during the disappearing animation. That probably doesn't hurt much, but depending on the application, something weird could happen like an alert view appearing after you've already left a view controller - which looks like a flicker to the user.
Anything non-view related, as indicated above, occur in the 'will' methods. The choice about will vs did, then, is really about what the user sees. Animations should start in viewDidAppear, otherwise, the user won't see the frames that occur during will/did appear. Data should be moved to views in viewWillAppear, otherwise, a blank view will transition in and the data will only appear after the transition animation completes. Also, it possible that a view frames could be adjusted in between viewWillAppear/viewDidAppear, like in the case of a previous view controller in the stack hiding/showing the navigation bar.
And on a side note, not something I will get into great detail here with, but I'd advocate against KVO for controller interactions that move data from model to view objects. Difficult to test and difficult to trace.
You can subclass your UILabel and in your subclass override the setText method:
-(void)setText:(NSString *)newText {
//do your KVO updates here
[super setText:newText];
}
hope this helps

Implementing openURL without knowing the current view state

I am implementing a custom URL scheme which will add entities to my data model. The details for the entity are contained in the URL. The basic idea is that an email link (or a link from another app) will open my app and add the new entity.
The problem is, I can never be sure which state my app will be in when responding. Any number of view controllers might be in view. If the list of entities is in view, I need to insert a new row for that entity. If other views are on screen, I need to react differently. Some views might also be modal.
I would be satisfied with a simple pattern when this happens - abort whatever the user is currently doing, and pop to the root view controller. From here I will probably push to a controller where I will show the new entity being added.
I experimented with always dismissing any modal displayed and popping to the root, with the benefit of not needing to know what exactly is being displayed:
[(UINavigationController *)self.window.rootViewController dismissViewControllerAnimated:NO completion:nil];
[(UINavigationController *)self.window.rootViewController popToRootViewControllerAnimated:NO];
This works reasonably well, but there at least two cases where it is insufficient:
If some object was created when the modal was presented (the modal is then used to modify the new object), and it is the delegate's responsibility to delete the object if the user cancels, the entity will remain alive.
If a UIActionSheet is being displayed, all bets are off. I can't dismiss this without knowing the controller that displayed it, having access to that controller, and sending it a message to dismiss the action sheet. Without doing so, the root view controller is popped to but the action sheet stays on screen. Subsequent taps on the action sheet of course cause a crash, since the controller that displayed it is gone.
How might I handle this robustly? Should I be trying to find out specifically which view controller is currently presented, and handling each scenario in turn? Or is there a more scalable solution that won't need updating each time I add controllers or change my application's flow?
It sounds like you are trying to do several things:
When the user clicks on your custom url, you want to add an "entity" to your model.
You want to display this new entity in some sort of EntityListViewController, which may or may not be on the ViewController stack.
You (may) want to pop off all view controllers above the EntityListViewController.
You want the user to know there was a new entity added (perhaps just by doing item 2).
You want to push some kind of EntityViewController, or if there is currently an EntityViewController in the view controller stack, you want to reload with the new entity's data.
It sounds like you have item 1 ready to go, since you didn't explicitly ask about handling the url click and inserting the new model object.
For the rest, a flexible and MVC-ish pattern would be to use NSNotificationCenter.
The code that inserts the new model object would "post" a notification:
[[NSNotifcationCenter defaultCenter] postNotificationName:#"entity_added" object:myNewEntity];
Then your various UI elements (e.g., UIAlertView and UIViewController subclasses) would listen for this notification and take some useful action (like closing themselves, or in the case of EntityListViewController or EntityViewController, reloading themselves).
For example, a UIViewController subclass might do this:
-(void) viewDidLoad
{
[super viewDidLoad];
[[NSNoticationCenter defaultCenter] addObserver:self selector:#selector(onNewEntity:) name:#"entity_added" object:nil];
-(void) onNewEntity:(MyEntity*)entity
{
// close, or redraw or...
}
-(void) dealloc
{
[[NSNoticationCenter defaultCenter] removeObserver:self];
// if not using ARC, also call [super dealloc];
}
To keep your life simple (and not worry too much about all the different UI states), I would consider doing this when the notification occurs:
Have the EntityListViewController redraw itself (does not matter if there something on top of it).
Show some sort of short-lived indicator in the nav bar (or somewhere else you know is always visible), or play a sound so the user knows that an entity was added.
And that's all.
If you take this approach, then there is minimal impact on whatever the user is/was doing, but when they do navigate back to the EntityListViewController it has all the new entities already displayed.
Clearly, if the click on the custom URL could possibly delete an existing entity, then it would be more important to pop off any viewcontrollers related to that entity. But this is also something you could do using the same pattern -- have the model or controller post the notification, and then have the various UI elements listen for it and take appropriate action.

Resources