handle view controller of storyboard in AppDelegate class - ios

I have a problem again. I work with storyboard in Xcode 6 with Swift as programming language. Before I want to present a view (with a view controller) on starting my app I want to test the internet connection and my server connection. If both connections are available I want to present a view1 and if not I want to present a view2. But I don't want to show a view with a spinner while checking the connection. So I thought I can manage it over the AppDelegate class. In the function func application() I want to decide which view (view1 or view2) is loaded at first. But for this solution I have to instantiate two view controllers that are connected to my two views in storyboard. I don't know if it is possible.
So my question, is it possible to instantiate these two specific storyboard view controllers in my AppDelegate class? And if it is possible, how can I do this by code?
If it is not possible, how can I solve my problem? At the moment I always show a view controller with a spinner (view0) and if connection is available I go to view1 and if connection is not available I go to view2 from my view0 controller like this:
override func viewDidAppear(animated: Bool) {
//some code
self.presentViewController(view1, animated: true, completion: nil)
//some other code
}

You can load a specific view controller from a storyboard with this call:
let viewController = self.window?.rootViewController?.storyboard?.instantiateViewControllerWithIdentifier("SomethingViewController") as UIViewController
self.window?.rootViewController = viewController
But to be honest, it doesn't seem good to make the user wait with no information while you do the connection tests.
It would be better to show a temporary view controller during the waiting, unless those tests are really fast :)
Edit: corrected the code.
It's a hackish solution, as it will load a new rootViewController after initializing the original one I suppose. But it should work.
A more correct alternative would be creating the project as "empty application" and loading the storyboard by code, directly in the controller you want.

Related

How to correctly dismiss previous view controller

Apologies as I know there are some similar questions, but I've been looking for two weeks through every one I can find, and cannot figure it out (I'm a bit of a novice).
I have a few different View Controllers, not using a Navigation Controller. I can segue between them no problem. The issue is, I need each view to be dismissed when I segue to a new one. Here is some of what I've tried so far.
Option 1 (in new View Controller)
override func viewDidAppear(_ animated: Bool) {
presentingViewController?.dismiss(animated: false, completion: nil)
}
Option 2 (in old View Controller)
override func viewDidDisappear(_ animated: Bool) {
self.dismiss(animated: false, completion: nil)
}
In both of these cases, the new view gets dismissed and I'm taken back to the old view. I've tried about 20 versions of similar code.
Should I be using the first VC in my program as my "main" view controller, and presenting/dismissing all others on top of it? I didn't think this approach seemed memory efficient, when the "main" VC is not often used after initially loading the app.
It seems like I'm missing or not understanding something. Any help would be greatly appreciated.
Think of it this way: A view controller can't exist on an island. It has to be presented on top of something.
That means when you present one VC on top of another, the presenting view controller is the "foundation" for the new one you just presented.
If you don't want to present VCs on top of each other, you have a couple of options:
1) Use a navigation controller. This is probably the best approach. You can present or push any view controller. If you decide to push, you can remove the old one from the navigation stack, or you can keep it there so the user can go back. There are lots of ways to use a navigation controller, and it's easily the most flexible way to navigate between controllers.
2) Use a tab bar controller. This works best if you have just a few different view controllers in your app, but it's good for certain use cases.
3) Do exactly what you said in your post (use the root view controller to present/dismiss all other VCs). As I said, you can't present a view controller out of thin air-- there always has to be something behind it. Unless there's a ton of stuff going on in your root VC, this shouldn't cause any memory issues. This approach should be fine unless you're very particular about the animations between your view controllers.
In general, I wouldn't worry too much about memory usage until it becomes a problem. It should be fine to present view controllers on top of each other for 99% of normal use cases.
if you want to present VC B from VC A and want to dismiss VC A while Presenting you can use this Code
let parentVC = presentingViewController
dismiss(animated: true) {
let vc = self.storyboard!.instantiateViewController(withIdentifier...)
parentVC.present(vc, animated: true)`enter code here`
} `enter code here`

Alarming memory increase with custom segue

I'm creating an app with XCode 6 (Swift) and recently managed to make a custom segue that doesn't use an animation. What I soon realised was that, when switching between two view controllers (tied together with the custom segues), the application memory usage (viewable with XCode) seemed drastically increase with every view controller switch. Is this something that I need to worry about? Does Swift automatically take care this memory increase, so that the app doesn't end up using an insane amount, or am I doing something wrong?
NoAnimationSegue.swift
import Foundation
import UIKit
#objc(NoAnimationSegue)
class NoAnimationSegue: UIStoryboardSegue {
override func perform () {
let src: UIViewController = self.sourceViewController as UIViewController
let dst: UIViewController = self.destinationViewController as UIViewController
src.presentViewController(dst, animated: false, completion: nil)
}
}
When you go from the first view controller to the second, you can presentViewController, but when you go back to the first, you do not want to "present" again. When you "present" one view controller from another, it keeps the first one in memory, so it's there, ready for you when you "dismiss" the second view controller to return back to the first.
Thus, if you repeat that process of presenting from one view controller to another in a circular fashion, you'll end up with multiple instances of the view controllers in memory. If you "present" to go from one view controller to another, then you'll want to unwind or dismiss to get back to the first view controller.
Alternatively, if you don't want to use the present/dismiss pattern, you can alternatively use tab bar controller if you want to jump back and forth between view controllers. Or you can use page view controller. Or you can use your own custom container view controller and replace the child view controller as you jump back and forth.
But just remember, if you present or push from one view controller to another, then you'll have to dismiss or pop to return back.

iOS UICollectionView navigation

I'm trying to figure out how to navigate around my app. But i'm a little lost.
I have a UIViewController that loads some data, then displays the data in a CollectionView. Then I have another UIViewController for the detailed view. I then trigger a segue to go to it, I pass the data etc.
self.performSegueWithIdentifier("detailViewSeque", sender: nil)
But the part i'm lost on is getting back to my main view, if I just trigger another segue then it loads all the data / view again. The data has already been loaded once, I really don't want to keep loading it.
I feel like I'm doing things wrong, that theres some super obvious way to handle this scenario.
Could someone point me in the right direction?
This is good situation to use an unwind segue (for more information: What are Unwind segues for and how do you use them?). Here's how to setup one up:
Firstly, create an #IBAction in the view controller you want to segue to, that takes a UIStoryboardSegue as its only argument. For example:
#IBAction func unwindToHere(segue: UIStoryboardSegue) {
// If you need you have access to the previous view controller
// through the segue object.
}
Secondly, you need to create the unwind segue in IB. To do this ctrl-drag from the view controller you want to segue from, to Exit and select the unwindToHere method:
Thirdly, you need to give your segue and identifier. To do this select your segue (see below - your segue will not be visible like normal segues); then use the Attribute Editor to give your segue an identifier.
Now you can use your segue. On the view controller you want to segue from, call:
self.performSegueWithIdentifier("YourID", sender: self)
To rephrase your needs "I have data that I need to keep around somewhere that isn't associated with a view controller".
You have a few options here. Your goal is basically to store it somewhere that isn't going to go out of memory.
The AppDelegate gets used for this purpose a lot but Singleton variable works as well.
I would personally create a singleton, say CatPictureRetriever with
private let _CatPictureRetriever SharedInstance = CatPictureRetriever()
class CatPictureRetriever {
static let sharedInstance = CatPictureRetriever()
var catPictures : NSArray?;
func gimmeCatPictures -> NSArray? {
return catPictures
}
}
Now you can get your pictures though your CatPictureRetriever anywhere
var pictures = CatPictureRetriever.sharedInstance.gimmeCatPictures()

How to initiate a viewcontroller without presenting it?

I have a tab bar controller and a few other viewcontrollers outside the tab bar controller. I have this viewcontroller called "X" which is a part of the tab bar controller. I have another viewcontroller called "Y" which is not a part of the tab bar controller. Now i want to initiate X when im inside Y upon tapping a button without actually presenting it. I want X to become active and fire its viewdidload so that i can access X whenever i chose to do so. Is this possible. Im sorry if im not clear in explaining my quiestion. let me know if you need any other additional information.
Old question at this point, but I was looking for an answer myself just yesterday and managed to get it figured out.
Instantiate the ViewController, then call loadViewIfNeeded().
Example:
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let exampleVC = storyboard.instantiateViewController(withIdentifier: "ExampleViewController") as! ExampleViewController
exampleVC.loadViewIfNeeded()
If you want, you can then check if the view is loaded with:
exampleVC.isViewLoaded
The ViewController is now all set up and ready for display when you decide to present it.
I want X to become active and fire its viewdidload so that i can access X whenever i chose to do so.
UIViewController uses lazy loading for the view property. You can just call:
[myViewController view];
This will trigger the loadView and/or viewDidLoad methods, if implemented.
However, you may wish to consider moving the relevant logic from viewDidLoad to init (or initWithCoder: if using a storyboard/xib). This way you won't have to call -view.
If I understand right, you want X to be initialised. So you can perform all you initialisation actions on your init constructor. viewDidLoad will only be called by the framework when you perform some presentation, either by pushViewController or addSubview. The reason for that is that because the framework wants to avoid getting instances of views on the memory without being used. So you can initialise all you want from your controllers but the views won't be loaded.

Objective-c Conditionally serve ViewControllers after app launches

I have a question about loading view controllers when an app launches. I want to have a condition that checks when the application launches based upon stored core data values and if true, it will load the view controller second in the stack. If false, I want to load the root view controller. I want to always preserve the root view controller not matter what the result of the condition, I just want to skip loading this view if the result of my condition is true and go right to the second view in the stack. I am not using storyboards. Has anyone done anything of this nature before?
Now, having said that, is this logic flow an acceptable solution to be implementing. Will there be issues during submission if I try something like this?
Let's say you have the following view controllers:
UINavigationController *navigationController;
UIViewController *firstViewController;
UIViewController *secondViewController;
Then you can write code like this (edit: reworked solution based on comments below):
if (yourCondition)
navigationController.viewControllers = #[ firstViewController, secondViewController ];
else
navigationController.viewControllers = #[ secondViewController, firstViewController ];
Early in your app's launch, like in -applicationDidFinishLaunchingWithOptions:, check the condition in question and, if true, push the second controller onto the navigation stack. Specify NO for the animation parameter so that there's no obvious transition.
You can also set the nav controller's viewControllers property directly. Set it to an array with first and second controllers if the condition is true, or just the first controller otherwise.
Check the conditions in the -applicationDidFinishLaunchingWithOptions: is the simple way.
However, we are suppose to keep the AppDelegate away from complicated. The best way to do the launching things, like check some condition, prepare the data, is to create a LaunchViewController as the rootViewController, you can make it the same looking as your app launch image. Then, you can presenting any VC you want with/without animation, it will be the first VC that user will see.
Let me know if you need more help.

Resources