for my app i want the app to animate loading progress (loading local CoreData, and syncing with game center) before entering main page.
So i created a LoadingView as my rootview, in the ViewDidLoad(), after the local loading and game center loading finishes, i then push to the main view.
I'm pretty sure it's the wrong approach since i'm getting this warning:
Presenting view controllers on detached view controllers is discouraged
Could you let me know what's a good practice?
I would suggest you create an initial root view which is very simple with a background and present your progress using a HUD. I use this one:
https://github.com/jdg/MBProgressHUD
When you have initialised everything, drop the HUD and replace the root view controller with a new one you load using storyboard. I would suggest you add a method to the app delegate you can call which does the switch for you.
You are then off and running with the new root. To change the root you use the following in your app delegate instance.
self.newController = <load with storyboard>;
self.window.rootViewController = self.newController;
[self.window makeKeyAndVisible];
I would use NSNotificationCenter to create an observer that hears an event when loading finishes:
You would place the observer in ViewDidLoad, more then likely, in order to remove the UIView that you instantiated when your CoreData load began. You will most likely need to construct a spinner view yourself. I usually use QuartzCore to do this and make sure it matches the rest of my app.
Because your core competency is not clear from your post, here is a NSHipster article that discusses the use of this concept: http://nshipster.com/nsnotification-and-nsnotificationcenter/
set your second view controller as root view controller. so you will not get any warning ect.
[UIApplication sharedApplication]delegate] window].rootviewController=secondViewCont;
only problem is there will not be any page navigation animation. You can observe with delegate or with notification to your loading process.
Related
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.
I'm currently building a little sample iOS app, i developed my UIViewControllers and views programatically, i'm targeting iOS 7+ devices and i have a simple question :
Here how i show a new controller
MySuperController *superController = [[MySuperController alloc] init];
[self.navigationController showViewController:superController sender:self.navigationController];
First i wanted to know if it's the correct way to show another view controller ?
Second imagine i'm performing those instructions in a LoginViewController that will be displayed just once (typically when the user launches the app) how can i release this loginviewcontroller after creating and showing another view controller ?
i know this question has been already asked but all the solutions presented are old/inappropriate (my sample app is ARC enabled which is by default enabled i think)
I'm new to this environnement any help/indication is appreciated thank you
As Roy Nakum says in his comment, if you are using ARC, your code is fine. You create your view controller using a local strong variable, then present it. At that point the navigation controller takes ownership of it. Since your strong reference is a local variable, it does not keep ownership after your method returns.
However there is another problem with your code. This line will likely cause you problems:
MySuperController *superController = [[MySuperController alloc] init];
You should not use init to create a view controller. It won't have any contents. You should either use initWithNibName:bundle: (to load a view controller from a NIB) or instantiateViewControllerWithIdentifier (to load a view controller from a storyboard.)
It is possible to set up a view controller so its "plain" init method loads it's views, but it takes special handling in the init method, and it's not the normal way to do things.
That's a good way to present a ViewController. If you have ARC enable (default) don't worry about releasing, it will release automatically.
Hi all I am doing a course in Udemy, and the code calls for placing code in the viewDidLoad function as shown below:
override func viewDidLoad() {
super.viewDidLoad()
placesArray.append(["name":"Taj Mahal", "lat":"27.175607", "lon":"78.042112"])
}
The array append should only run once, however, when I segue to another viewController and come back, it runs the code to append again. So I now have an array with 2 rows, both of which are Taj Mahal.
I thought that the viewDidLoad function only runs code once?
Is there a way around this?
Thanks.
Addendum:
I am using Swift, so I don't see any alloc and init while creating and launching the viewController. And weird as it sounds, the video tutorial has it working in the viewDidLoad and the trainer is using the storyboard to segue from the initial table view controller to a map view on a view controller and just has a back button on the map view that segue's back to the table view controller via the storyboard as well. - Could be because I have the latest version of the Swift language and the trainer was using an earlier version, (cause I noticed some slight differences in coding earlier) but you never know. Either way whenever he touches the back button it does not run the append code anymore.
I am trying to get in contact with the trainer as some of the suggestions here, though they are good don't seem to work.
I will put the solution in here once I get in contact with the trainer.
The viewDidLoad method is called when your view controller's view finishes loading. The view will load when a view controller's view property is nil and something attempts to access it.
UIViewController *myVC = [[UIViewController alloc] init];
UIView *aView = myVC.view; // this loads myVC's view; viewDidLoad is called when it completes loading.
If the view has unloaded (usually due to memory limitations), it will be called when the view property is next accessed.
Manipulation of data sets should generally not be done within view methods. Consider moving this to the init of the view controller (or to a different "datasource" class).
I suppose you are trying to do data initialisation in viewDidLoad. If there is no other operation on placesArray before viewDidLoad, then instead of append, what about setting the placesArray directly:
placesArray = ["name":"Taj Mahal", "lat":"27.175607", "lon":"78.042112"]
Then even if your view is unloaded for some reasons. Taj Mahal will still be added once only.
viewDidLoad is called whenever the view controller's view property is set. When does this happen? It depends on how the view controller is contained:
UINavigationController
- View Controller views are loaded as they are added to the navigation stack and "unloaded" (although the viewDidUnload method is deprecated) as they are removed.
UITabBarController
- View Controller views are loaded as they are added to the tab bar regardless of whether they are on screen or not. They stay loaded as you change from tab to tab.
Depending on your needs and use case, you can create your own view controller container that does what you need. Checkout the Apple docs on the proper way to do this:
https://developer.apple.com/library/ios/featuredarticles/ViewControllerPGforiPhoneOS/CreatingCustomContainerViewControllers/CreatingCustomContainerViewControllers.html
My app runs some tasks in the background, then in the end shows an alert asking the user if she wants to send the result by email.
In other places of the app I use the MFMailComposeViewController in the same way as it should be (shown e.g. here). Here however, as I am in the completion block of a potentially long running task, I have no reference to a UIViewController instance I could use for the following calls:
...
mailSendingController.mailComposeDelegate = self;
[self presentModalViewController:mailSendingController animated:YES];
I could probably keep the reference to the VC that invoked the task, but it might be dismissed before the task finishes.
What can I do? Create a fake hidden VC just to serve as the basis of the MFMailComposeViewController? Is there a way to show MFMailComposeViewController without an underlying VC? Use somehow the VC that is currently on top of the stack?...
Any ideas are welcome.
First of all, presentModalViewController:animated: is deprecated as of iOS6 [1] you should use presentViewController:animated:completion: [2]
I would just get the app's root view controller and then use that to present the modal view controller. That way it will be on top no matter what and you know the root view controller is always there.
Look at this question for some hints on accessing the app's root view controller.
Also, if you are going to do GUI stuff make sure you go back to the main thread:
dispatch_async(dispatch_get_main_queue(), ^{
// Present the VC here
});
Good luck!
use ]
[UIApplication sharedApplication].keyWindow.rootViewController presentViewController: mailSendingController]
I've got a question regarding the two mentioned methods, since in my tests I don´t make clear the order they are called. I thought that, firstly, viewDidLoad is called when the viewController is loaded for first time (as the name indicates), and inmediately after the init method. Then, I thought that once viewDidLoad returns, viewWillAppear is called. If you display another viewController, and then you return to this one, then it should be already loaded and only viewWillAppear will be called.
However, while developing I make the impression that there is no order when calling viewDidLoad and viewWillAppear... I couldn´t find a clear description of this lifecycle in Apple's documentation, how does this actually work?
Thanks!
I would like to add to Caleb's answer: Don't confuse the view controller and the view! The name viewDidLoad clearly indicates that the method is invoked after the view has been loaded. It is the view controller that does the loading.
Some pointers regarding the lifecycle of views and the order in which messages are sent:
Not an official Apple document, but I find this diagram really useful because it includes pretty much all of UIViewController's lifecycle overrides.
In the section Resource Management in View Controllers from Apple's "View Controller Programming Guide" there is a flowchart that depicts how views are initially loaded. It explains loadView and viewDidLoad, also in conjunction with storyboards.
The section Responding to Display-Related Notifications from Apple's "View Controller Programming Guide" explains how to respond to views appearing and disappearing (viewWillAppear: et al)
If you are planning on implementing a container view controller: The UIViewController class reference has a good overview of how messages need to be sent by your subclass.
I'm stopping here. You can find more stuff yourself by googling for "uiviewcontroller life cycle".
-viewDidLoad is called when the controller loads its view, which is not necessarily right after initialization. View controllers don't load their views until they need them, either to display or for any other reason.
-viewWillAppear is called just before the view is displayed. This will be after -viewDidLoad, but you don't know exactly how long after. -viewWillAppear is called every time the view is displayed; -viewDidLoad will only be called a second time if the view is unloaded at some point (such as didReceiveMemoryWarning). These days that's unusual, but it can happen.
Or if the viewController is set to nil, which can usually happen if a view controller is kicked off the navigation stack, and therefore next time it is brought to the navigation stack it needs to call -viewDidLoad again.
I thought that, firstly, viewDidLoad is called when the viewController
is loaded for first time (as the name indicates), and inmediately after the init method
No. The name indicates that the controller's view has been loaded (not the controller itself). Actually the docs state that this method will get called after the view hierarchy has been loaded into memory (either via loadView or through a nib for example).
Then, I thought that once viewDidLoad returns, viewWillAppear is
called
Again, no. loadView (and as a consequence viewDidLoad) method will get called the first time that view property is to be accessed and is nil (which is the case when you're initializing a controller). Think of this simple scenario:
MyViewController *vc = [[MyViewController alloc] init];
UIView *view = vc.view; // <= loadView & viewDidLoad will fire but it certainly didn't appear...
However, while developing I make the impression that there is no order
when calling viewDidLoad and viewWillAppear...
Well there is an order. We know for sure that viewWillAppear will always be called after viewDidLoad (if both of them are to be called of course).
As you said, ViewDidLoad is only calling once after loading the view. So we can initialize the instances in the viewDidLoad. It is mainly meant for the initialization.
viewWillAppear will invoke whenever we reach to this view. So if there is any changes in UI, we can done it in viewWillAppear.
I ran a trace on when all these calls are made: http://thecodist.com/article/ios_arc_storyboards_and_uiviewcontroller_trace