iOS Swift - Data is not fetched quickly enough to reach another UIViewController - ios

I have a source view controller and a destination view controller.
I would like to show data fetched from a url in the UI (destination) just after moving to this screen ("show" segue).
In order to do that, in the source VC I use "prepare for segue" method, where I call a function that returns an array with all the fetched data I want to show and pass it to the destination VC to be shown in a UITableView.
The problem is that many times the whole data is not fetched from the url, so I pass an empty array to the destination.
This is the code in the source VC:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destinationVc = segue.destination as?
ShowCharacterInfoViewController{
fetchCharacterArrayFromUrl(characterName: cellContent){ results in
destinationVc.array.append(contentsOf:results)
destinationVc.tabelView.reloadData()}
} }
I can't think of a proper solution.

You can do one of two things.
Load the data and only perform the segue after the function has returned the data.
Transition to the destination screen and have that controller load the data in viewWillAppear.
// Add a place to save the results
var saveResults: MyResultsType?
#IBAction func someButtonAction(_ sender: Any) {
// Consider putting a "Loading..." dialog here
fetchCharacterArrayFromUrl(characterName: cellContent){ results in
self.saveResults = results
DispatchQueue.main.async({
// Hide the "Loading..." dialog
performSegue(withIdentifier: "ID of your Segue", sender: sender) //
})
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destinationVc = segue.destination as? ShowCharacterInfoViewController {
// I'm not sure append() is really what you need here
destinationVc.array.append(contentsOf: savedResults)
destinationVc.tabelView.reloadData()
}
}

You need to update the UI stuff in main thread since you are executing backend call:-
DispatchQueue.main.async({
destinationVc.tabelView.reloadData()
})

Related

reset variables when pass to an another ViewController

my problem is that when i switch to an another ViewController, my variables of the previous VC call are reset so i can't do what i want after
#IBAction func BackBtn(_ sender: Any) {
self.nbrQst = 10
self.Switch1A = 2
performSegue(withIdentifier: "Numero", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "Numero" {
let vc = segue.destination as! ViewController
vc.nbrQt = nbrQst
}
if segue.identifier == "Numero" {
let vcNv = segue.destination as! ViewController
vcNv.Switch1 = Switch1A
}
}
below the way that i send information from my Lvl1 file to the lvl selector file to add a if else for unblocks the lvl
#IBAction func BackBtn(_ sender: Any) {
self.nbrQst = 10
self.Switch1A = 2
performSegue(withIdentifier: "Numero", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let vc = segue.destination as! ViewController
vc.nbrQt = nbrQst
vc.Switch1 += 2
}
that's the second ViewController who send the number to the first one, and i want that the first one remember the number and add them up each time I press the button that sends the numbers
Let's call "Selector VC" the main VC.
The reason why those numbers reset in main VC is that when you press the "BackBtn", you're not actually going back to main VC, you're creating a new main VC and going there (because you did performSegue(withIdentifier: "Numero", sender: self) in BackBtn function, performSegue always takes you to a new VC rather than taking you back). The new main VC has all those fields starting from an initial number (I assume initially they're zero).
What you want to do is to go BACK, not forward. Depending on how you went to the level VC from main VC, you have different ways of going back to main VC.
If you're pushing everything onto a navigation controller, then you can go back by:
self.navigationController?.popToRootViewController(animated: true)
If you're presenting everything modally, you can go back by
self.dismiss(animated: true, completion: nil)
You said, you want to update the value in main VC so that you can block/unblock next level. A very standard way to achieve this is via delegate, please Google this ("delegates in Swift") and follow their tutorials there.
I strongly recommend that you take an online course on swift development before you go ahead and develop your own app, because you could potentially do a lot of things wrong and waste a lot of your time trying to get an answer from Stack Overflow.

View controller with 2 destinations

I have 2 buttons in a view controller. If button 1 is clicked, then go to WebView controller. If button 2 is clicked then go to ArtView controller.
How to make override func prepare(for segue: UIStoryboardSegue, sender: Any?) have 2 destinations depending on the button clicked?
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destination = segue.destination as? WebViewController {
// your code
}
if let destination = segue.destination as? ArtViewController {
// your code
}
}
Hope it will help! :)
Have each button invoke a different segue, either by control-dragging segues directly from the button, or by calling performSegue(withIdentifier:sender:) from your buttons' IBAction code.
Then use code in prepare(for:sender:) to figure out which destination is being invoked.
You can:
Check the identifier on the segue (not recommended - fragile)
Check the class of the destination view controller (better than 1)
Have each view controller implement a protocol and check the protocol of the destination:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
switch segue.destination {
case let webView as WebViewProtocol:
//Your code for a web view
case let artView as ArtViewProtocol:
//Your code for an Art View
}
}
Option 3 is reliable while providing loose coupling (each case can trigger any view controller that conforms to the desired protocol. All it needs to know is that the destination understands the desired protocol.)

How to data in segue using Swift

I'd like to pass my array from my first to my second ViewController.
In the end of my first VC I tried to pass it like this:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let secondVC = ViewController2()
secondVC.passengers2 = passengers
print(secondVC.passengers2)
print(passengers)
}
"passengers" is my first array in "ViewController.swift" and "passengers2" my sewcond array in "ViewController2.swift".
When I go over to my second VC, the console tells me, that "passengers" and "passengers2" have the same value, but as soon as I am in "ViewController2.swift", "passengers2" is emtpy for some reason.
Does anyone know why?
The problem in your code is that you instantiate a new ViewController2 object, which is never used.
You want to use the destination view controller that is inside your segue object, like that :
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destinationVC = segue.destinationViewController as? ViewController2 {
destinationVC.passengers2 = passengers
}
}

Programmatic embed segue

I'm using a Page View Controller to implement an image carousel in my app. I have a container view in my parent view controller which has an embed segue to the Page View Controller. The problem I'm having is when I need to go off and fetch these images asynchronously and the segue happens before viewDidLoad gets the images.
My prepare for segue:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "startCarousel" {
let carouselPageViewController = segue.destination as! CarouselPageViewController
carouselPageViewController.images = carouselImages
}
}
in viewDidLoad:
GalleryManager.sharedInstance.getData = {
(data) in
// Assign images to instance property here ...
}
My thinking is that once I get the images that I programmatically start the segue myself. Or if I should be doing this a different way any advice would be much appreciated.
You can do something like, return false in shouldPerformSegue and initiate segue when data is loaded
i.e.
override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
if identifier == "startCarousel" {
return (detail != nil)
}
return true
}
Then in viewDidLoad
GalleryManager.sharedInstance.getData = { [weak self]
(data) in
self?.performSegue(withIdentifier: "startCarousel", sender: nil)
}
No, you can't delay an embed segue. If you use an embed segue it fires as soon as the view controller is loaded.
You need to wait until the data loads and then call the setViewControllers(direction:animated:completion) to install the view controllers that you will use to display the data once the data is loaded.
Swift 3, Xcode 8
You can realize in this way:
set a reference to your page view controller in prepareForSegue in your masterViewController:
var myContainerViewController: UIPageViewController?
override func prepare(for segue: UIStoryboardSegue, sender: Any?)
{
if segue.identifier == "embedSegue" {
if let destination = segue.destination as? YourPageViewControllerClass {
self.myContainerViewController = destination
}
}
Define a function in your own pageViewController class:
func someFunction(){
//configure your image or swipe page
}
when you get the images, in your masterViewController call:
self.myContainerViewController?.someFunction()
you can't delay an embed segue.
you try to put button in view controller and then after view load set button click to set image...

Restore View State with segue ios

I have setting like this:
First controller reads data and sets in Second Controller.
Second Controller has a button that executes push segue to Third Controller with a countdown.
After countdown I want to go back from third controller to second that should still have data. Is there any way to achieve this?
Also I am using Storyboards.
I send data to Second Controller in prepare func. Second to Third controller change is connected directly to button in Storyboards.
override func prepare(for segue: UIStoryboardSegue, sender: AnyObject?) {
let controller = segue.destination as! QuestionViewController
controller.questionIDs = sender as! [Int]
}
To come back from Third to Second controller I use this code from Third controller:
self.performSegue(withIdentifier: "nobodyAnsweredSegue", sender: self)
Change this:
self.performSegue(withIdentifier: "nobodyAnsweredSegue", sender: self)
To this:
self.navigationController?.popViewControllerAnimated(true)

Resources