perform segue on application enter foreground with multiple views? - ios

I would like to move back to my main view using a segue. When the home button is pressed and the app enters the background.... then later the user wishes to open the application again bringing it to the foreground I would like a segue to be run which takes them from whatever view they were in back to the 'main view'. I dont mind using multiple segues... or how could I access these segues in the app delegate for example.
What is the best method for this? Thankyou! beer and points for everyone who gives a good answer ;).

You may implement the applicationWillEnterForeground:application: method (from the UIApplicationDelegate protocol). This method is called whenever your application's state changes from backgo to inactive state (before it goes to active).
In this method you could call performSegueWithIdentifier:identifier sender:. If you have a segue from each possible view back to your main view, your are done.
However, unless your application has a very complex state that would need lots of time to reconstruct, I would recommend to do general a re-initialization of your application in application:didFinishLaunchingWithOptions: or applicationDidBecomeActive:. In this way it is easier to preserve a consistent state.

Related

How to search for iBeacons when using view controllers

I am a new iOS developer so please bear with me if I seem a little ignorant.
I am trying to make an app for a gallery. I want to use iBeacons in each category of the gallery. I know how to make this all happen if I am only using one view controller. I can make it range for the beacon and once in a certain range send a pop up stating "You have entered the "XXXX" area. Would you like to view these exhibits?"
If the user presses yes, it will take them to it. However, the problem I am running into is that I need to have quite a few view controllers. So How can I get the ranging to happen in the background of all of them? Should I create a separate Swift file and do all the ranging there and then call that class in each view? Or should (or even could) I do all the beacon code in the app delegate.
The next problem that I have is how to segue to the "category" view controller when a user is in a view controller that doesn't have the actual segue? Say VC 1 has a segue to VC 2 with identifier #3. Can I call the segue with ID #3 from VC 5, for an example? Or is it better to instantiate the views?
Will appreciate any tips.
This kind of centralized logic fits well into the AppDelegate as it is designed to handle global app logic. It is easy and a common practice to set up beacon monitoring and ranging in the didFinishLaunching callback.
To launch specific view controllers, it is easiest to instantiate and present them programmatically (rather than using a segue) when a change is needed based on beacon detection in a callback in the AppDelegate.

When is an app removed from memory

One of the tabs in my app presents blog posts. I notice that when I move to another tab or leave the app that when I return, new blog posts are not downloading. The download is kicked off by viewDidLoad() in the ViewController. It's not firing when I return to this view.
Why isn't viewDidLoad() firing when I leave the app? How long does the app remain view loaded in memory?
How should I check for new posts when the user comes back to the app or from another tab?
Thanks!
viewDidLoad may not be the best place to download the updates. If for example you push from ViewController A -> ViewController B, the first view controller (A) isn't unloaded.
You may want to put the code in viewDidAppear or viewWillAppear.
Look at ViewController LifeCycle for some reference.
You can use the applicationDidBecomeActive notification to trigger updates or whatever else you want your app to do when it is brought back from the background.
There is a good answer on this here: How can I use applicationDidBecomeActive in UIViewController?
There is also a good article on Apple's website about handling transitions from various app states here: https://developer.apple.com/library/ios/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/StrategiesforHandlingAppStateTransitions/StrategiesforHandlingAppStateTransitions.html
ViewDidLoad is only called once when the ViewController is instantiated. In a UITavBarController, the child view controllers are only instantiated once. As you move from tab to tab, the ViewControllers are kept in memory.
https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITabBarController_Class/
If you background the app, then iOS will keep it in memory until it starts to get low, then starts to terminate apps.
Take a look at the delegate for the TabBar
https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITabBarControllerDelegate_Protocol/index.html
This can tell you specifically when the user switches tabs. But if they flip back and forth, it could needlessly create several API requests.

How do I jump to an arbitrary scene in a complex storyboard from a push notification?

Let's say I have a relatively complex storyboard with a login screen guarding access to a UITabBarController containing a couple of embedded UINavigationControllers and their children, like so:
The LoginViewController is the root VC, and decides if the user is logged in, either by checking stored credentials or asking for fresh ones. It then presents the UITabBarController as a modal.
There are four scenarios:
1) If the user starts the app from cold, they should see the ListViewController.
2) If the app is started from cold via a push notification regarding a "Foo", they should go directly to the FooDetailViewController.
3) If the app is warm-started via a push notification regarding a "Foo", they should go directly to the FooDetailViewController, no matter what VC they were looking at when the app was backgrounded.
4) If the user warm-starts the app, they should go back to whatever VC they were looking at when the app was backgrounded.
Where does the logic for all of this decision-making go? If I put it in the AppDelegate, I end up trying to push view controllers onto my hierarchy before they exist (Warning: Attempt to present ViewControllerX on ViewControllerY whose view is not in the window hierarchy!). The UITabBarController's viewWillAppear: lifecycle method does not seem to be being called on a warm start, and putting logic in every child view controller to check if it's been started from a push seems like coupling things that shouldn't be coupled (and in practice seems to lead to corrupted navigation stacks.)
How does everyone else structure this?
You must create that logic inside UIApplicationDelegate by creating properties needed to decide what is the view controller that has to be opened first.
The first place you have that guarantees the user interface (the window) is ready, is on the application delegate's -applicationDidBecomeActive:, but from here there are two courses of action:
For ListViewController and OtherListViewController is easy, you can just call -setSelectedViewController on UITabBarController;
For FooDetail and BarDetail, I think the best method is putting some logic on ListViewController, sending it a reference to the object you want to show the detail and by performing the relevant segue on ListViewController's -viewDidAppear:.

Nil out a presentedViewController when UIApplication becomes active again

I have a presentedViewController on top of the root view controller when the app gets dismissed. (e.g. User navigates to another app or goes back to the home screen.)
I would like to nil it out when the user reactivates the app without it being visible to them. Calling -dismissViewControllerAnimated: is not an option because it only works if the view controller is visible, and I'd like to do it sooner and specifically only in application:openURL:sourceApplication:annotation: and otherwise let the user continue their workflow in the modal view.
Are there any tricks I can use?
According to your comment, the only actual issue is what happens when the app receives application:openURL:sourceApplication:annotation: and is thus brought to the front.
In that case, simply adjust your interface in application:openURL:sourceApplication:annotation: - that is part of its purpose, to allow you to do that. You will be given some time before the "snapshot" is torn away, so if the adjustment involved is to dismiss an existing modal view, dismiss it without animation and there you are.
The user will still see the modal view for a moment, but not really - what the user is seeing is not your interface but the "snapshot" that was created by the system when the app when into the background. The "snapshot" is removed to reveal the actual interface (with a nice crossfade effect in iOS 7).
If the "snapshot" itself is problematic there are ways of preventing it from being taken, but it doesn't sound to me like this is that kind of situation.
(By the way, this raises the question of how the presented view is to know that it must be dismissed. The initial main action is in your app delegate, which is probably some conceptual distance away from the presented view. This would be an appropriate situation in which to use an NSNotification to communicate across this distance.)

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.

Resources