Passing Data through multiple View Controllers with segue [Swift 3.0 Xcode] - swift

NOTE: Question has been edited in an attempt to be more clear with my issue.
Hey, I am trying to pass data through multiple VCS. I want to pass data (arrays) from V2 -> V3 and then V3 -> V1 but I want to be able to only navigate through the VCs as such: V1 - V2 - V3 and V3 - V2 - V1.
So what I need to learn is how to pass data without navigating to a different VC as well as setting up two preparetosegue methods to pass data between V2 -> V3 and V3 -> V1 while also being able to navigate between all VCs. When I create my first preparetosegue, I am unable to use other segues associated in my VC to navigate to other VCs without getting a Fatal Error.
Can anyone help me?
Any input would be greatly appreciated!
Heres my attempt:
import UIKit
class ViewController: UIViewController {
var name = String()
var StopButInfo = [String]()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
import UIKit
class SecondViewController: UIViewController {
var StringArray = [String]()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBOutlet var PHeight: UITextField!
#IBOutlet var PName: UITextField!
#IBAction func Search(_ sender: Any) {
if PHeight.text != ""{
performSegue(withIdentifier: "SearchSegue", sender: self)}
let CDstart = String(describing: Date())
StringArray.append(CDstart)
StringArray.append(PName.text!)
StringArray.append(PHeight.text!)
}
override func prepare(for SearchSegue: UIStoryboardSegue, sender: Any?){
let thirdController = SearchSegue.destination as! ThirdViewController
thirdController.SearchButInfo = StringArray
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
import UIKit
class ThirdViewController: UIViewController {
var height = String()
var SearchButInfo = [String]()
var StringArray = [String]()
#IBAction func Stop(_ sender: Any) {
if StringArray.count != 0{
performSegue(withIdentifier: "SegueToStart", sender: self)
}
let CDStop = String(describing: Date())
StringArray.append(CDStop)
StringArray.append(height)
}
override func prepare(for SegueToStart: UIStoryboardSegue, sender: Any?){
let firstController = SegueToStart.destination as! ViewController
firstController.StopButInfo = StringArray}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}

You need to check which view controller's segue is about to be performed, this can be done like this inside prepare(for segueMVH: UIStoryboardSegue, sender: Any?) method:
if let firstController = segueMVH.destination as! FirstViewController {
// Set first view controller's data
} else if let secondController = segueMVH.destination as! SecondViewController {
// Set second view controller's data
} else if let thirdController = segueMVH.destination as! ThirdViewController {
// Set third view controller's data
}
Note: It's better to use a different identifier for each segue to be able to distinguish between them.

The error you got on let secondController = segueMVH.destination as! ThirdViewController is saying that you are casing HC.ViewController into ThirdViewController and that's why it failed. It means that the destination view controller of your segue is not ThirdViewController.
To further help you understand segue: A segue is a connection between a source view controller and a destination view controller and can be only used between them two. See this picture:
In this case, I have a segue connection in my storyboard connected between my VC1's button and VC2. In this case, my button click will trigger this segue and prepare(for segue) method can only be in VC1 where it passes data to VC2.
So in your situation, you believe that the destination of your segue is ThirdViewController but it's actually not. So please check your segue with identifier segueMVH to see if it is connected between your current view controller and your ThirdViewController.
Hope this helps

Related

Passing Data between view Controllers Using a segue from a view embedded in a navigation controller to a tabbarcontroller

I have two views that I would like to pass data from one view to the next. The first view is where I have the data that I would like to pass to the next view lets call it SourceViewController. However SourceViewController is embedded in a NavigationViewController and the secondViewController lets call it DestinationViewController is the firstView in a TabViewController.
I have tried to use the answer from this question and it fails to go past navigation view it just skips the whole logic.
This is my code :
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "loginSuccessSugue") {
if let tab = self.presentingViewController as? UITabBarController,
let nav = tab.viewControllers?[0] as? UINavigationController,
let destinationVC = nav.viewControllers.first as? HomeViewController {
destinationVC.currentBalance = serviceBalance
}
}
}
This is the HomeViewController:
class HomeViewController: UIViewController , UITableViewDelegate,UITableViewDataSource, UICircularProgressRingDelegate{
var currentBalance = 0.0
override func viewDidLoad() {
super.viewDidLoad()
circularBalance.maxValue = CGFloat(currentBalance)
print(currentBalance)
}
override func viewDidAppear(_ animated: Bool) {
print(currentBalance)
circularBalance.setProgress(value: CGFloat(currentBalance), animationDuration: 3)
}
}
This is how the storyboard looks like:
This is my view controller where you can check that I am sending 5 to tabbar first viewcontroller:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.performSegue(withIdentifier: "segueIdentifier", sender: self)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let barViewControllers = segue.destination as! UITabBarController
let destinationNv = barViewControllers.viewControllers?[0] as! UINavigationController
let destinationViewController = destinationNv.viewControllers[0] as! FirstViewController
destinationViewController.currentBalance = 5
}
}
Now You can check my firstview controller where you can check that what value we are getting.
class FirstViewController: UIViewController {
var currentBalance = 0
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
print(currentBalance)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Now, You can check my console and storyboard:
From Apple's UIViewController docs:
var presentingViewController: UIViewController?
The view controller that presented this view controller.
Which would work great for you, ** IF ** you were trying to go back in your navigational hierarchy, as did the guy in the SO post you referenced.
You are trying cast the VC THAT PRESENTED SOURCEVIEWCONTROLLER of your SourceViewController as a UITabBarController, which fails miserably, and is why you never hit a breakpoint inside your nested if let's.
If we look the next variable down from this in the docs we can see something that will take us forward to the UIViewController we are presenting:
var presentedViewController: UIViewController?
The view controller that is presented by this view controller, or one
of its ancestors in the view controller hierarchy.
So now to go over the code you need to solve your predicament. I'll give you the same code you posted, but fixing the tense of my verbs in the comments:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "loginSuccessSugue") {
//ing -> ed
if let tab = self.presentingViewController as? UITabBarController,
let nav = tab.viewControllers?[0] as? UINavigationController,
let destinationVC = nav.viewControllers.first as? HomeViewController {
destinationVC.currentBalance = serviceBalance
}
}
Isn't it frustrating when the English language tricks you up more than swift?
EDIT:
Since you are passing the data in prepareForSegue: you will actually want to get the UITabBarController from segue.destination. And since the the UITabBarController's ViewControllers property will be nil or empty in prepare for segue. This is a bad approach for passing the data.
You may need to create custom subclass of UITabBarController, pass it the variable, and then pass that data to its viewControllers in viewDidLoad.
class MyTabBarController: UITabBarController {
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
var serviceBalance : Double?
override func viewDidLoad() {
super.viewDidLoad()
//Make sure vc is not null or empty before continuing.
guard let vcs = viewControllers, !vcs.isEmpty else {
return
}
if let navVC = vcs[0] as? UINavigationController, let destinationVC = navVC.viewControllers[0] as? UIViewController {
destinationVC.serviceBalance = destinationVC
}
}
}
Updated prepareForSegue:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let tabBarVC = segue.destination as? MyTabBarController {
tabBarVC.serviceBalance = serviceBalance
}
}
Don't forget to change the UITabBarController's class in the identity inspector of storyboard to MyTabBarController
You need to change if() condition code.
Use below code will get your HomeViewController in destination of segue.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "loginSuccessSugue") {
if let destinationVC = segue.destination as? HomeViewController {
destinationVC.currentBalance = serviceBalance
}
}
}
As in segue.destination you will get your HomeViewController so no need to get it from Tab + Navigation stack.
Edit:
let destinationVC = segue.destination as? HomeViewController
Print(destinationVC)
Hope this solution will helps!

Unable to send an int value between view controller and table view controller in swift

the class PreferencesViewController sends nil through prepare for segue regardless if IBActions are taken or not. It seems that something is wrong in my prepare for segue but I'm not sure. I'm not trying to assign to any label or anything I'm just trying to send the int value to another variable in the class Pick2_1. Thanks in adavance
import UIKit
class PreferencesViewController: UIViewController {
var preference = 0
#IBAction func nImportant(sender: UIButton) {
preference = -1
print(preference)
}
#IBAction func N(sender: UIButton) {
preference = 0
print(preference)
}
#IBAction func mImportant(sender: UIButton) {
preference = 1
print(preference)
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "preferenceSegue" {
let preferenceVC = segue.destinationViewController as! Pick2_1
preferenceVC.preferenceSent = preference
print(preference)
}
}
}
Ok segue is directly connected to the button. You have to set the segue between your 2 controllers set the identifier and in the #IBAction you should call performSegueWithIdentifier func

Delegate using Container View in Swift

I'm developing an app for iPad Pro. In this app, containerView use to add additional views and interact with them.
First, I created a protocol:
protocol DataViewDelegate {
func setTouch(touch: Bool)
}
Then, I created my first view controller
import UIKit
class ViewController: UIViewController, DataViewDelegate {
#IBOutlet var container: UIView!
#IBOutlet var labelText: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
}
func setTouch(touch: Bool) {
if touch == true {
labelText.text = "Touch!"
}
}
}
And finally, I created a view that will be embedded in containerView.
import UIKit
class ContainerViewController: UIViewController {
var dataViewDelegate: DataViewDelegate?
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func touchMe(sender: AnyObject) {
dataViewDelegate?. setTouch(true)
}
}
But for some reason, nothing happened, the first view controller receives nothing in setTouch function.
My question is: In this case, using container, how can I make the communication between two ViewsControllers?
Like #nwales said you haven't yet set the delegate. You should do set the delegate in prepareForSegue function on your first viewController (who contain the viewContainer)
First select the embed segue and set an identifier in the attributes inspector.
Then in the parentViewController implement the func prepareForSegue like this:
Swift 4+:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "the identifier") {
let embedVC = segue.destination as! ViewController
embedVC.delegate = self
}
}
Below:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
if (segue.identifier == "the identifier") {
let embedVC = segue.destinationViewController as! ContainerViewController
embedVC.dataViewDelegate = self
}
}
Looks like you defined the delegate, but have not set the delegate. This happens to me all the time.

Swift - passing text and int from vc1 to vc 2 (uitextfield -> label in vc2)

Passing only input text from textfield to label in second VC works!! But i want when user type number 10 in uitextfield, ( 1 ticket is 2 euros so 10 tickets * 2 euro is 20) and when i click PAY button, so that SUM can be displayed in label in second VC, i think that viewdidload in VC2 is happening before prepareForSegue, i don't know. It works when i click second time on PAY button, but not when i first click PAY button where label displays zero, help :) Embedded in navigation controller for navigation.
VC1
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var howManyTickets: UITextField!
var sumTicketsAndPriceOfTickets = Int()
var priceOfTicket = 2 // euros
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func platiTeKarte(sender: AnyObject) {
sumTicketsAndPriceOfTickets = howManyTickets.text.toInt()! * priceOfTicket
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let driver = segue.destinationViewController as! primaocViewController
var whatToPass = sumTicketsAndPriceOfTickets
driver.receiver = whatToPass
}
}
VC2
import UIKit
class primaocViewController: UIViewController {
#IBOutlet weak var displaySum: UILabel!
var receiver:Int!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
self.displaySum.text = String(receiver)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
If your button is wired directly to a segue, then you don't need an #IBAction as well. As you are seeing, the prepareForSegue is happening before the #IBAction for your Pay button. Just compute your value in prepareForSegue:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let driver = segue.destinationViewController as! primaocViewController
driver.receiver = (howManyTickets.text.toInt() ?? 0) * priceOfTicket
}
I changed the calculation of the pay to use the nil coalescing operator ??. This is generally a safer approach because if the toInt() returns nil for any reason, it will in this case just use 0 instead of crashing.

Popovers in Storyboard, how to handle the delegate

In a storyboard i have a view controller which has a segue to another viewcontroller, with the "Present As Popover" applied. If i don't add any code, this works as id expect.
Issue:
I need to get the delegate working so i can get data back from the popover. I have created the delegate in the popover.
I have added
class LoginView: UIViewController,UIPopoverPresentationControllerDelegate, UIPopoverControllerDelegate,KeypadDelegate
I have added the functions to my main view controller.
Issue i have is how to set the delegate on the segue.
i have tried
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "KeyPadLoad"
{
let popoverViewController = segue.destinationViewController as! UIViewController
popoverViewController.modalPresentationStyle = UIModalPresentationStyle.Popover
popoverViewController.popoverPresentationController!.delegate = self
}
}
The segue is defiantly called KeyPadLoad but when the delegate functions should be called in the popover nothing happens.
What am i doing wrong
Thanks
You have to implement the UIPopoverPresentationControllerDelegate in the class that presents the Popover. The methods of the UIPopoverPresentationControllerDelegate protocol let you customize the behavior of a popover-based presentation.
If you have set a segue to go to the Popover and you set the segue as Present as Popover then your class has to be like this :
class ViewController: UIViewController, UIPopoverPresentationControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
var popOver = segue.destinationViewController as! PopOverViewController
popOver.popoverPresentationController!.delegate = self
}
// Tells the delegate that the popover was dismissed.
func popoverPresentationControllerDidDismissPopover(popoverPresentationController: UIPopoverPresentationController) {
println("dismissed")
}
}
Where the class PopOverViewController is just an UIViewController that handle the Popover, no more, something like this code :
class PopOverViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
With the above code you should see the message:
dismissed
Every time that the PopOverViewController is dismissed.
I hope this help you.