Dismiss or destroy all view controllers, action sheets, etc? - iphone

I'm implementing a custom URL scheme in my navigation based app to let users import data from an email, and so I have this method in my app delegate:
- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url
It would be nice, if after importing the data in that method, I could use popToRootViewController to go back to the rootViewController to show the user the new data.
However, that doesn't work if the user had imported the data while a modal view controller or action sheet was up (popToRootViewController doesn't dismiss those, and causes the app to freeze).
Is there any way I can safely dismiss/destroy all view controllers/action sheets/alert views except for the rootViewController?
Or perhaps some way to tell from within my app delegate if the user has an action sheet or modal view controller up?
Or, do I just leave them where they left off, and not provide any immediate indication that data was successfully added?

You'll have to keep track of them yourself. An easy way to do that is to post a notification, with a reference to the modal view/sheet/alert in the userinfo dictionary, every time you display something modal. Whenever a modal view returns, post a different notification.
Observe the notifications in your app delegate (or wherever it makes sense to do so, but the app delegate is simplest). When you receive the first one, store a (weak) reference to the modal view, and zero the reference when you receive the second one. In between, you'll be able to respond to -application:handleOpenURL: by dismissing the modal view if your reference is non-nil.
Note also that in the case of alerts, it's probably best to dismiss them when you enter the background. Weeks might pass before you are foregrounded again, and it's bad style to greet the user with "Are you sure? OK/Cancel" when they may no longer have any idea what you're asking about.

Related

How can be used phone locks as a action trigger in Swift?

I want to change to another viewcontroller in swift when the phone is locked. Is there any variable or function?
When the user locks their phone 'applicationWillResignActive' and then 'applicationDidEnterBackground' from the App Delegate get called and then when the user unlock it - 'applicationWillEnterForeground' and 'applicationDidBecomeActive' get called. You can either implement your view controller to be pushed in the App Delegate or add a notification observer in your view controller to react to any of these and push/pop/modally present the view controller you need. It all depends on your implementation - it is a bit of a broad question. Hope this helps!

Returning to main view after phone call in iPhone

I know that I should use this when I get an interruption in the app and it comes back:
- (void)applicationDidBecomeActive:(UIApplication *)application
But how would I, when the app becomes active again, dismiss any previously opened view controllers and present the main view controller?
This may depend on your architecture. If you use an UINavigationController to manage your views, you could call popToRootViewController on it to push everything else off the navigation stack and return to the root view (you'd have to maintain a property or something similar in your application delegate). If you have any modal views open, you could maintain a property in your application delegate that contains the currently active modal dialogue (if any) which would be dismissed.
Note though that applicationDidBecomeActive: is also called when the application was put into the background (e.g. because the user went for another app in the meantime) and the user returns to your app (as well as on first launch), so returning to the root view may be something different than the user expects.

Presenting Modal View Controller login screen

In my app there is authentication required, so when you launch one of the tabs on tab bar, "class A" checks are there credentials saved if not, "class B" modal view controller with fields to login launches.
So my question is : in which method in class A (loadView, viewWillAppear or maybe in another one) should be implemented checking if there are credentials saved and other stuff described above.
And my additional second question is:
is pushing modalviewcontroller correct way to show login screen, or i should do that differently?
Thank you for reply guys.
OH ! One More Thinh
And one more thing. I've done implementing LoginView by adding delegate and presenting ModalVC (Harkonian the Piquant's method). But in my tab bar app i have got very confusing problem. I mean when user taps login button (assume that everything was correct and he's able to secured data) how PROPERLY switch to tab where is secured info. I mean previously selected tab.
i did it by adding in
-(IBAction) login {
//some code
self.tabBarController.selectedIndex =1;
And it seem to work good but is it correct ?
I have a very similar use case in my app -- it requires a passcode to authenticate. After a lot of testing and tweaking I found the following design to be the best approach:
Don't use class A to launch your credentials VC -- use the app delegate instead.
For security purposes, typically you'll want the credentials VC to show before the user can view the underlying view. It's much easier to handle this in the app delegate than in a VC. In addition, you need to consider what happens when your app is backgrounded -- a screen shot is taken of the current state of the app. If you are using viewController A to show the credentials view, when the app relaunches the user will be able to see whatever sensitive information was visible on app close until the app finishes launching and VC A presents the credentials VC.
Don't insert your credentials view into an existing ViewController -- use a new UIWindow instead.
You don't ever want any other view to be able to sit on top of your credentials view. Ever. Even views that would normally always be on top, like UIAlertView. The easiest way to achieve this is to have a special UIWindow just for your credentials view. Show this window and hide the primary app window whenever you need to display the credentials view.
How does this approach look in practice?
If you are at all interested in how well this design works, you can check out the passcode feature in Audiotorium Notes for iPad. I spent a lot of time with this design to make sure it was as secure as possible.
If you have any specific implementation quests feel free to ask and I'll try to answer them.

How to reset an iOS application state after a user clicks sign out?

So I posted a question a while back which can be seen at: How to reset an iOS application when a user clicks the "sign out button"?
Following the advice I made a sign out button where a user where by is taken to the main screen where they can register or sign in again. What I am finding out is that when a new user signs in, I am seeing certain value from the old user in pickers, UITextView ETC
Is there a way to reset application state or do I have to go the long route of making sure that each outlet is set to default values? Is this a sign of bad programming practice somewhere?
The Cocoa way = KVO (key value observing). Controllers interested in being informed about login state change register themselves as observers on login component/controller/whatever instance does the login.
After login/logout this component notifies all observers about state change. Those then do all necessary actions: populating UI with user data after log in or resetting them after log out.
Very flexible pattern that avoids unnecessary dependencies between components.
Ok first thing you have to do is make your main view implement the delegate for signout successful. In this you got to reset all the data and views which will be recreated/repopulated when a new user signs in.
Inorder to achieve this you can analyse your code/logic as to what is created on a new user sign in and reset all these in the delegate method for sign out. This is way to generic an answer but resetting the data is the way to do it.
Alternatively you can recreate your main view when sign in is successful i.e. remove the main view and on sign in success create it afresh for the new user.
The cleanest way would be to create a new set of view controllers and set them to the viewControllers of the UITabBarController object but it needn't be the cheapest always. This will be something that you need to check if it's viable or not.
Otherwise, you'll have to to consider adding reset methods to the view controllers. If the tab is a navigation controller, popToRootViewControllerAnimated: and reset the first view controller. This one is a bit of effort to implement compared to the former approach.
When ever you go to new controller just allocate the whole controller again. and dont forget to release once the navigation is done.
Heres the sample code example to do it
-(void)goToFormController
{
FormViewController *objFormViewController = [[FormViewController alloc]initWithNibName:#"FormViewController" bundle:nil];
[self.navigationController pushViewController:objFormViewController animated:YES];
[objFormViewController release];
}
Happy iCoding...

Objective-C object dealloc'd while other objects still has a delegate reference to it causes crashes. How to prevent this?

I have an app with a navigation controller as the root view. There are many views that can be pushed in.
The user has to create an account to use the app. The user can then log into this account from other devices, but only one device can be logged onto the same account at a time. So if multiple devices try to log into an account, only the latest device will be logged in and the other devices are logged off (a push is sent to the devices).
Since there are multiple views that the device could be showing before it was logged off, I call popToRootViewControllerAnimated: to get back to the root view. This is because when the user logs in the next time I only want the root view to be shown (the new account might not have access to the previously shown view).
If the user has an alert view or action sheet presented (which uses the current view as its delegate) before the push is received, the view will still be shown after the popToRootViewControllerAnimate: method is called. If the user then taps on a button for the alert view or action sheet, it will send a message to the dealloc'd view and crash the app.
An example:
myViewController is being shown to the user.
myViewController create an action sheet prompting the user for a decision.
The push is received for the device to log out.
The navigation controller pops all the views controllers and now shows myRootViewController.
Since the view controllers are popped, myViewController is now dealloc'd.
The action sheet from myViewController is still shown.
When the user selects an option form the action sheet, a message is sent to myViewController, and since it is already dealloc'd, a crash will occur.
Is there any way to prevent this?
One solution I have considered would be to keep track of all the objects that uses a specific view controller as its delegate. Then when that view controller dealloc's it will also set all the object's delegates to nil. This requires me to manually take care of every view controller when they create an object that uses itself as the delegate, since I cannot think of a way to automatically create and update this list.
Any better solution (or improvement to mine) would be appreciated!
Edit: The alert view and action sheet are only examples of some objects that I would use myViewController as a delegate. I am also using a number of other classes (and third-party libraries) that implements this delegate pattern.
A few ideas:
you can encapsulate the alert/action sheet view and delegate in a single class. Then when you need an alert view, create MyAlertView instead, which will also be its own delegate and will do [self release] after the user taps a button.
make your App Delegate the only delegate for all your alert views and action sheets. App Delegate is always around while the application is running, so there won't be a problem with a released delegate.
The problem with both solutions is that if you need your application to know what happened in the alert view/action sheet, you somehow need to tell the interested class of the user's choice.
You can do that by either using delegates of your own - which would mean you're back to square one - or use notifications: when the alert view/action sheet delegate is called, it would post a notification ([[NSNotificationCenter defaultCenter] postNotificationName:NotificationName object:self userInfo:userInfo];), while the interested object would look for that notification ([[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(onNotification:) name:NotificationName object:nil];) and perform whatever tasks necessary in onNotification:(NSNotification*)aNotification method.
You'll be able to agree with yourself on what type of information is passed in those notifications (I would think the button number in a NSNumber class would be enough, or perhaps pass the button text, too). And you won't have to keep track of all alert views - just don't forget to remove the observer ([[NSNotificationCenter defaultCenter] removeObserver:self name:postNotificationName object:nil];) in the views' dealloc.
Edit:
"This requires me to manually take care of every view controller when they create an object that uses itself as the delegate, since I cannot think of a way to automatically create and update this list."
Actually you probably can do this in a semi-automated way: make a singleton object with a method like
-(id)delegate:(id)delegate for:(id)forWhom
And then instead of
someThingy.delegate = self;
you'd do
someThingy.delegate = [[DelegateLocker defaultLocker] delegate:self for:someThingy];
Inside the DelegateLocker you'd have a MutableDictionary with delegate class as a key and a MutableArray of someThingies as a value. Then in your view controllers' deallocs you'd call
[[DelegateLocker defaultLocker] delegateIsDying:self];
which would go through the thingies and assign delegate = nil for each
The drawback of course is that you'll be retaining all the thingies for an indefinite period of time instead of releasing them immediately.
So the ViewController that presented the action sheet iand set itself as the delegate right? So why dont you keep a reference to the ActionSheet in the ViewController, in the dealloc method of the view controller, you can check if the action sheet is visible, if it is then set the delegate of the action sheet to nil,and dismiss it...
so
-(void)dealloc
{
if(myActionSheet && [myActionSheet visible])
{
[myActionSheet setDelegate: nil];
//dismiss
}
}
Hope that helps
If you want automated solution, I think you can make a function to iterate through Ivars of your view controller to see if any Ivar has delegate property and set it to nil.