Swift 4, load next ViewController - ios

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

Related

Displaying map view with navigation?.push(mapView) and not seeing anything pop up

I am trying to display a MapKit view when the user clicks a button where MapScreen is of class UIViewController and MKMapKitdelegate.
When I perform this sender on the UIButton all I am getting is the background color of the other screens in my TabBarController.
let mapController = MapScreen()
navigationController?.pushViewController(mapController, animated: true)
I would expect to see the map as is defined in MapScreen(). I am using this same method to display many other view controllers and it works great.
If your view controller defined in storyboard you should not use plain init to initialize an object. As official documentation states:
When using a storyboard to define your view controller and its associated views, you never initialize your view controller class directly. Instead, view controllers are instantiated by the storyboard either automatically when a segue is triggered or programmatically when your app calls the instantiateViewController(withIdentifier:) method of a storyboard object. When instantiating a view controller from a storyboard, iOS initializes the new view controller by calling its init(coder:) method instead of this method and sets the nibName property to a nib file stored inside the storyboard.
So, In order to create view controller from storyboard you should call instantiateViewController(withIdentifier:) function of UIStoryboard object:
let storyboard = UIStoryboard(name: "MainStoryboard", bundle: .main)
let viewController = storyboard.instantiateViewController(withIdentifier: "Your view controller identifier")
Hope this helps.

Unwind segues with programmatically created view controllers

I have a view controller (PhotoViewController) which has a child view controller (CameraViewController). In CameraViewController, there is a button that sets off a chain of segueing through other programmatically created view controllers. On the last of these view controllers, I want to unwind to the original PhotoViewController. All examples I have found require you to have view controllers in the storyboard, which I don't have. How can I do this?
You have not understood what an unwind segue is. It is merely a way of reversing the calls that created the current view controller and got it into the view controller hierarchy, in the special case where you want to trigger this through a segue in a storyboard.
For example, if you (or the storyboard) called pushViewController (for a pushed view controller), an unwind segue is just a way of calling popViewController.
Or, if you called (or the storyboard) called present (for a modally presented view controller), an unwind segue is just a way of calling dismiss.
If you don't have your view controllers in a storyboard, you don't need an unwind segue; you just do one of those two things, directly, in code.
Remember, we all got along for years just fine without unwind segues. In fact, we all got along for years without storyboards! And so can you.
If your original PhotoViewController is created on a storyboard and you want to segue back to it from a programmatically made VC you have present it rather than unwinding because unwindSegues are for storyboards only.
To present your PhotoViewController here is one of the ways to do so.
func presentPhotoViewController() {
let storyboard = UIStoryboard(name: "YourStoryBoardName", bundle: nil)
if let photoViewController = storyboard.instantiateViewController(withIdentifier: "PhotoViewControllerUniqueId") as? PhotoViewController {
// Pass any data you have (Optional)
self.present(photoViewController, animated: true, completion: nil)
// If your PhotoViewController is embeded in a navigation Controller do the following.
//let navController = UINavigationController(rootViewController: photoViewController)
//self.present(navController, animated: true, completion: nil)
}
}

SWIFT 3: Segue with navigation controller

I'm trying to send some values from one viewcontroller to another. (all embedded in NavigationController). I could make segue with normals viewcontrollers so in general I understand a segue idea. I've to do that in two different ways and got two different problems.
First way, segue does right but "two times". I mean after segue (which I want) there is another segue to same controller but without navigation controller and without data I would send. The "back" button on last viewcontroller is returning to AlmostViewController.
Here's code:
Second way, Nothing happened,
ErorCould not cast value of type 'RevisionApp.AlmostViewController'
(0x1055d58c0) to 'UINavigationController' (0x107669f18).
Here's code:
For the first Problem, When using embedded in navigation controller you have don't have to create action function when the button is tapped. Navigation controller does it for you. So things you need to do :
Disconnect the function btnTapped from the button using storyBoard.
Delete or comment the function btnTapped(You don't need it).
Problem is line number 18. You are trying to cast AlmostViewController to a UINavigationController. Directly access like this,
let detailController = segue.destination as! AlmostViewController
let storyBoard : UIStoryboard = UIStoryboard(name: "Main", bundle:nil)
let nextViewController = storyBoard.instantiateViewControllerWithIdentifier("ViewController2") as NextViewController
self.presentViewController(nextViewController, animated:true, completion:nil)

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.

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