Change view controller when user exits app - ios

Hey I am trying to make it so whenever the user presses the home button and then try to returns to my app, they are returned to the initial view controller. Is there anything I can write into the app delegate to get this to work?
I have tried to call this function in the applicationDidBecomeActive function:
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let VC = storyboard.instantiateViewControllerWithIdentifier("homeVC") as! UIViewController
self.window?.rootViewController?.presentViewController(VC, animated: true, completion: nil)
But i get an error saying the view is not in the hierarchy.
Any help will be appreciated!

You want to implement the applicationDidBecomeActive method of your App's delegate, and programmatically perform a segue or dismiss the current view controller, depending on the navigation setup.
Your users might not like forced navigation though. The general assumption is that they can resume where they left off.

Well, every time the app is closed applicationDidEnterBackground in your AppDelegate is called. You can use that to present your view controller.

Related

Swift 4, load next ViewController

In my app I have two View Controllers embedded in Navigation Controller. And I want to have an option to get data from the SecondVC while only the FirstVC is opened. So it's like I don't want a user to see (or at least interact) with the SecondVC.
The point is
So user just taps on the button on the FirstVC (without opening SecondVC before) and I can get access to the data on the SecondVC.
How can I get data from the next view controller (SecondVC) in a navigation controller without opening it? Or at least not letting user to interact with it.
For now my only ideas are:
To push segue when user taps a button, get it into memory, load data and then dismiss it. But that doesn't look good when view controllers change so fast.
To present next view controller modally offscreen, get data and dismiss it. So the user won't even see that another view controller opened.
Edit:
I need to get access not just to method or smth. I need to get to the #IBOutlet. To the subclass of UIView (working with CorePlot) to be exactly
You should never call loadView() directly.
Instantiate your UIViewController and then call loadViewIfNeeded().
Calling this method loads the view controller’s view from its storyboard file, or creates the view as needed based on the established rules.
EDIT:
let storyboard = UIStoryboard(name: "Main", bundle: nil)
// make sure that the value is an optional so that you can unset it later. This one is optional because we use optional casting (with as?)
var vc = storyboard.instantiateViewController(withIdentifier: "secondVC") as? SecondViewController
vc?.loadViewIfNeeded()
// use the vc
// later unset vc variable
vc = nil
for this, you must call your 2ed View controller with 'modalPresent' and background of 2edVC must clear. Then get your data from 2ed VC. with no change. for more information about modalPresent there is the link.
Solution:
You can create a function for it or just insert in viewDidLoad() etc.
Don't forget to change StoryboardID for the chosen view controller (it can be done in your storyboard file in Identity inspector).
// You need to get to your Storyboard and instantiate the needed View Controller
let storyboard = UIStoryboard(name: "Main", bundle: nil)
var graphVC = storyboard.instantiateViewController(withIdentifier: "graphVCID") as? graphVC
// Then call loadViewIfNeeded (don't call loadView()) to load #IBOutlet's etc
graphVC?.loadViewIfNeeded()
// My outlets are loaded with data when these functions are called
graphVC?.viewDidLoad()
graphVC?.viewWillAppear(false)
// Unset the reference to VC
graphVC = nil

SetViewControllers with uiPageViewControllers is not working

I am making an app that uses a UIPageViewController for navigation between pages. Everything works fine except that when I am trying to manually change which view controller is shown using setViewControllers it does not show.
I know that it is creating the view controller because it prints to the log, but the controller shown on screen isn't changed. The indexes I use for the subclass of uiviewcontroller that makes each view controller are also set correctly.
The function is called on the press of a view controller.
Here's the code that is supposed to change the controller:
func changeVC(VC: UIViewController) {
setViewControllers([VC], direction: .Forward, animated: true, completion: nil)
}
And the function call:
Let controller = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("contentView") as! ViewController
Controller.index = row
PageViewController().changeVC(controller)
That is inside of the didSelectRowAtIndexPath method of a table view.
Cjay was absolutely correct,
Lemme explain it little more in detail to help you understand what mistake you are doing.
Everytime you call PageViewController().changeVC(controller) new instance of PageViewController will be created and changeVC will be called on that new instance that you have just created. Though your code to instantiate a ViewController from storyboard setting it's index,calling changeVC and setting theViewController of PageViewController everything executes you won't see any of this making any effect on your screen because you changed the ViewController of the PageViewController which you just instiated which neither has a visible valid frame (0,0,0,0) nor it is loaded (not added to your ViewController's view) :)
Rather what you should have done is to do exact same things on a PageViewController which is already loaded :)
As per you comments you are loading the PageViewController from storyboard create an IBOutlet for it lets call it as myPageViewController :) so when you want to change the ViewController of it simply say,
let controller = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("contentView") as! ViewController
controller.index = row
self.myPageViewController.changeVC(controller)
Hope this helps :)

swift - how to force the app to show a particular view after return from background

Ok, i know that when applicationWillEnterForeground fires from my AppDelegate that I can trigger events. What I would like to do is force the app to show a certain view when It re-appears from the background. The view is a UIViewController called loginViewController and it has a storyboard Id of "initViewController"
My question is, what do I use in this function (applicationWillEnterForground) to make this view load when the app comes into focus again? Thanks.
I've never done this, but you can probably accomplish it by adding the UIViewController to the UIViewController stack, if you just want to add a view controller and show its view (see Case 1) or by replacing the root view controller, if you want to discard the existing view controller stack and use a new one (see Case 2)
Case 1) In the first case you need a reference to the UIViewController that you want to make its parent view controller. You can save this in a static variable somewhere or if you are just planning on showing a temporary view when the app restarts then you can get a reference to the root view controller use it as the parent:
// get a reference to the main storyboard
let mainSB = UIStoryboard(name: "Main", bundle: nil)
// get a reference to the root view controller
if let rootVC = UIApplication.sharedApplication().keyWindow?.rootViewController,
// get a reference to the view controller using identifier
initVC = mainSB.instantiateViewControllerWithIdentifier("initViewController") as? UIViewController {
// present the view controller
rootVC.presentViewController(initVC, animated: false, completion: nil)
}
When you are finished use logic from within the view controller to dismiss it, and your user should be back where they started:
self.dismissViewControllerAnimated(true, completion: nil)
Case 2) You can discard the existing view controller stack and start a new one by replacing the root view controller and building up the stack manually. Present the view controllers in order with animated parameter set to false.
// get a reference to the main storyboard
let mainSB = UIStoryboard(name: "Main", bundle: nil)
// get references to view controllers
if let vc1 = mainSB.instantiateViewControllerWithIdentifier("vc1") as? UIViewController,
vc2 = mainSB.instantiateViewControllerWithIdentifier("vc2") as? UIViewController {
// set root view controller
UIApplication.sharedApplication().keyWindow?.rootViewController = vc1
// build up the view controller stack by adding next vc
vc1.presentViewController(vc2, animated: false, completion: nil)
}
Navigation Controllers
If one of your view controllers is a navigation controller you will need to cast it as such, and then push any view controllers onto your navigation controller. Navigation controllers have their own stack.
if let myNavCon = mainSB.instantiateViewControllerWithIdentifier("nav") as? UINavigationController {
// push view controller onto navigation controller stack
myNavCon.pushViewController(someViewController, animated: false )
}
WARNING
This does not deal with the model of your app at all (only the UI). You will also need to set any data that you would have set in prepareForSegue, etc. An easy system to use when you have VCs you present both programmatically and via storyboard segues is to take the code that would have been in prepare for segue and move it to your own instance method that takes a reference to the child view controller as its parameter. Then you can call it from prepare to segue with the destination view controller or from code before you present the view controller.
None of this code has been tested. It was written directly via the website. It likely contains typos. Please let me know so I can fix any.

Dismissing ViewController Scenario

I am new to Swift programming. I am stuck in one scenario.
When my app loads, the first screen gets in ViewController1 user's latitude and longitude and then gets the data from a backend API and loads the data in a table view controller. This code of getting user's location and loading table view controller is in ViewDidLoad method. In this ViewController1, I have a button when tapped, I am presenting a view controller (Which is embedded in a navigation controller) modally. The following lines of code I am using to present this new ViewController (ViewController2).
let VC2 = self.storyboard?.instantiateViewControllerWithIdentifier("NewVC2") as! NewVC2
let navController = UINavigationController(rootViewController: VC2)
VC2.delegate = self
self.presentViewController(navController, animated: true, completion: nil)
When I close the ViewController2 by saying self.dismissViewController, it closes and when the control comes back to ViewController1, ViewDidLoad is executed again and all the code of invoking backend API and getting user's location is getting invoked.
So, for passing data back from ViewController2 on dismissing it, I used the delegate pattern. Created a protocol and defined a method in this protocol.
protocol ViewControllerClosedDelegate {
func isViewControllerClosed(closed: Bool);
}
defined the object of delegate in ViewController2.
var delegate: ViewControllerClosedDelegate! = nil
and added the following code while dismissing ViewController2.
delegate.isViewControllerClosed(true)
self.dismissViewControllerAnimated(true, completion: nil)
made the ViewController1 to implement this ViewControllerClosedDelegate and added the following function.
func isViewControllerClosed(closed: Bool) {
self.isFromClosedVC = closed
print(isFromClosedVC)
}
isFromClosedVC is a global variable defined ViewController1.
the value is printing correctly (true in this case) as being passed from ViewController2 when it is dismissed but when I try to print this in ViewDidLoad, it is being printed as false.
I thought of getting this value from ViewController2, and if it is true, not to execute the code to get users location and data from backend API.
Basically, I am trying to get the user's location and getting the data from backend API once and update my table view only on Swipe to refresh.
How do I achieve this? Please help.
Thank you.
Raj

Pass data with segue without displaying screen

I'm trying to achieve passing of data using segue, but without displaying the screen. There are two view controllers in my project and I will name them FirstViewController and SecondViewController.
On FirstViewController, I am using Show Adaptive Segue to pass data using performSegueWithIdentifier. The prepareForSegue method will be called, and recognising the identifier I've set to display SecondViewController.
However, the problem is I do not want to display SecondViewController. There are other things that my users may want to do before heading for SecondViewController.
I'm new to iOS programming, and I only know passing of data with segue. Please do share with me if there are methods to pass data apart from segue.
EDIT: To further elaborate my question. I'm working with TabBarController, and both View Controllers are accessible on the Tab bar. So when I am on SecondViewController with some data already "segue-ed" over from FirstViewController, I can head back to FirstViewController to add more data. At SecondViewController with UIRefreshControl I need the updated data.
If you do the segue in code you can do what you want in a following way
1) Instantiate view controller from the story board
let storyboard: UIStoryboard = UIStoryboard(name: "SecondViewController", bundle: nil)
let vc = storyboard.instantiateViewControllerWithIdentifier("set_it_in_IB") as! SecondViewController
2) Set the properties you need to set as the view controller is already instantiated
vc.someProperty = "asd"
3) Segue!
viewController.presentViewController(viewControllerToPresent: UIViewController, animated: Bool, completion: (() -> Void)?)
Hope that helps!
From your user's perspective, they would not know whether the data is being updated before they segue or not, so it is probably fine to just pass the data with the prepareFoSegue function.
Otherwise, you could try setting a notification to the viewDidLoad on your FirstViewControllerand SecondViewController by
NSNotificationCenter.defaultCenter().addObserver(self, selector: nil, name: updateSecondVCNotification, object: nil)
and posting the notification from the FirstViewController whenever the data needs to be updated using the
NSNotificationCenter.defaultCenter().postNotificationName(updateSecondVCNotification, object: self)
Then on second view controller set the the selector of the addObserver to a string of the name of whatever function you want it to do like "reloadNewData"
func reloadNewData() {
tableView.reloadData()
}
Does this help? Don't forget to set the updateSecondVCNotification as a global string constant at the top of your FirstViewController. Learn more about NSNotificationCenter at https://www.andrewcbancroft.com/2014/10/08/fundamentals-of-nsnotificationcenter-in-swift/
Woo! Notifications! \(^▽^)/
I'm not sure if that would work for you, but you could simply keep the data in memory of your FirstViewController for the time during which the user is still interacting and giving feedback and once it's done, send all the data to the SecondViewController through the segue.

Resources