Segueing from Child ViewController - swift

I have a situation where I am presenting a child view controller "B" onto another view controller "A". View controller "B" has 5 buttons which segue(push) to a third view controller, "C". The problem is once I'm at view controller "C", I want to be able to use an unwind segue to go from C-->A without B showing up in the middle.
// view controller A class
class AViewController: UIViewController {
#IBAction func goToViewControllerB(_ sender: UIButton) {
let viewControllerB = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "viewControllerB_ID") as! BViewController
self.addChild(viewControllerB)
viewControllerB.view.frame = self.view.frame
self.view.addSubview(viewControllerB.view)
viewControllerB.didMove(toParent: self)
}
#IBAction func unwindToStart(segue: UIStoryboardSegue) {
print("back from view controller C!")
}
}
//view controller B class
class BViewController: UIViewController {
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let identifier = segue.identifier {
switch identifier {
case "segueFromBToC_1":
if let destVC = segue.destination as? CViewController {
//pass data
}
// ... cases 2-4
case "segueFromBToC_5":
if let destVC = segue.destination as? CViewController {
//pass data
}
default: break
}
}
}
}
// empty CViewController class
I know that putting the following code in my view controller B class will remove it from the parent view controller, "A", but then I can't segue from "B" to "C" anymore as "B" doesn't exist.
self.view.removeFromSuperview()
self.removeFromParent()
self.willMove(toParent: nil)
I was wondering where I should put the above code or if I should segue from a child view controller at all? I also haven't used navigation controllers as I don't know how to implement them with a child view controller. Should I have #IBAction outlets for each of the 5 buttons and put the above code in there?

UPDATE: I was able to fix it by detaching the 5 push segues from the buttons and have each of the 5 push segues go directly from view controller B to view controller C . I then had an #IBAction for each button where I had the following code in view controller B:
class BViewController: UIViewController {
#IBAction func button1ToVC3(_ sender: UIButton) {
segueAndRemoveSelf(segueName: "segueFromBToC_1")
}
#IBAction func button2ToVC3(_ sender: UIButton) {
segueAndRemoveSelf(segueName: "segueFromBToC_2")
}
#IBAction func button3ToVC3(_ sender: UIButton) {
segueAndRemoveSelf(segueName: "segueFromBToC_3")
}
#IBAction func button4ToVC3(_ sender: UIButton) {
segueAndRemoveSelf(segueName: "segueFromBToC_4")
}
#IBAction func button5ToVC3(_ sender: UIButton) {
segueAndRemoveSelf(segueName: "segueFromBToC_5")
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let identifier = segue.identifier {
switch identifier {
case "segueFromBToC_1":
if let destVC = segue.destination as? CViewController {
//pass data
}
// ... cases 2-4
case "segueFromBToC_5":
if let destVC = segue.destination as? CViewController {
//pass data
}
default: break
}
}
}
}
extension BViewController {
func segueAndRemoveSelf(segueName: String) {
self.performSegue(withIdentifier: segueName, sender: self)
self.view.removeFromSuperview()
self.removeFromParent()
self.willMove(toParent: nil)
}
}
Not sure if this is best practice though.

Related

Passing variable in swift with segue

I'm using segue to open a new window in a xcode app.
The segue is correct, when I tap on the button the new window is shown.
I want to pass a string variable from FirstViewController to DetailViewController but I can't set the variable in the DetailViewController.
This is the code in FirstViewController:
if control == view.rightCalloutAccessoryView {
func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let controller = segue.destination as! DetailViewController
controller.Name = "test"
}
performSegue(withIdentifier: "showdetail", sender: self)
}
And this the DetailViewController code
class DetailViewController: UIViewController, WKNavigationDelegate {
var Name: String = ""
override func viewDidLoad() {
super.viewDidLoad()
print(Name)
}
}
Where am I doing wrong?
The method prepare(for must be on the top level of the class.
And please name variables according to the guidelines with starting lowercase letter
func someMethod()
{
if control == view.rightCalloutAccessoryView {
performSegue(withIdentifier: "showdetail", sender: self)
}
}
func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let controller = segue.destination as! DetailViewController
controller.name = "test"
}
}

Attempting to pass data from one viewcontroller to another using segues

I have two viewcontrollers setup and set the segue connections as well. I'm attempting to pass data from one of the VC's to the other. Using the below code and using the func override works. What i want to do is only have the override func prepare fire off when a button is pressed and when i paste the override func code into a button action, it doesnt work - the label on the first VC doesnt update - it stays to the default value i set.
First ViewController code:
var testLabel1:String = "default"
#IBOutlet weak var testLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
testLabel?.text = testLabel1
// Do any additional setup after loading the view.
}
Second ViewController code:
override func prepare(for segue: UIStoryboardSegue, sender: Any?)
{
if segue.destination is ViewController
{
let vc = segue.destination as? ViewController
vc?.testLabel1 = "Success"
}
}
Button action that ive tried
#IBAction func actionBtn(_ sender: Any) {
func prepare(for segue: UIStoryboardSegue, sender: Any?)
{
if segue.destination is ViewController
{
let vc = segue.destination as? ViewController
vc?.testLabel1 = "Success"
}
}
}
You need to trigger it with performSegue
class FirstVc:UIViewController {
#IBOutlet weak var lbl:UILabel!
#IBAction func goToSecond(_ sender: Any) {
self.performSegue(withIdentifier:"YourSegueName",sender:nil)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "YourSegueName" {
let vc = segue.destination as! SecondVC
vc.testLabel1 = "Success"
}
}
func setData(_ str:String){
self.lbl.text = str
}
}
class SecondVC:UIViewController {
var testLabel1 = ""
weak var delegate:FirstVc?
func goBack() {
self.delegate?.setData("FromSecond")
self.dismiss(animated: true, completion: nil)
}
}

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!

How to know which segue was used using Swift?

I have a main viewController and a detailsViewController. The detailsViewController has 2 buttons. Both buttons are segues back to the main controller but I want to customize the main viewController based on which segue was used. What is the best way to check which segue was used to reach a viewController so that the main viewController can be customized depending on that? - if segue1 leads to the the main viewController then I want label1 hidden. if segue2 leads to the main viewController, then I want label2 hidden.
In Main View Controller create a variable , something like
var vcOne : Bool = true
Now in DetailsViewController
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if segue.identifier == "segue_one"
{
let mainVC : MainViewController = segue.destinationViewController as! MainViewController
secondVC.vcOne = true
}
else if segue.identifier == "segue_two"
{
let mainVC : MainViewController = segue.destinationViewController as! MainViewController
secondVC.vcOne = false
}
}
Now in MainView Controller
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
//Now check here for which segue
if(vcOne)
{
// implement for button one click
}
else
{
// implement for button two click
}
}
Hope it helps you
I'd do something like to check which segue was used. You have to set an identifier to the segue in the storyboard though!
func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "yourIdentifier" {
let yourVC = segue.destinationViewController as? yourViewController
//do magic with your destination
}
}
There is a option for setting Identifier for segue. This should be unique identifier. So that you can identify which segue is activated.
Ex:
func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "Identifier1" {
let firstVC = segue.destinationViewController as? FirstViewController
} else if segue.identifier == "Identifier2" {
let secondVC = segue.destinationViewController as? SecondViewController
}
}

Goto ViewController From UICollectionViewCell Class Swift

How can I go to a UIViewController from my UICollectionViewCell Class?
Here is my UICollectionViewCell Class :
class GalleryItemCollectionViewCell: UICollectionViewCell,UIAlertViewDelegate
{
#IBAction func gotoView(sender: AnyObject)
{
//I Want To Do It HERE...
}
}
self..window.rootViewController.performSegueWithIdentifier("nameOfSegueInStoryboard", sender: self)
This will transition to a view controller of your choice. To change variables in that view controller, you can use the prepareForSegue method under the view controller that contains the collection view:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "nameOfSegueInStoryboard" {
let controller = segue.destinationViewController as! NameOfYourViewController
controller.variable_name = new_value
}
}
This all relies on you adding a segue from your first view controller to the other and giving it an identifier in your storyboard.
You will need to add a seguey to the storyboard then trigger it programmatically:
self.window.rootViewController.performSegueWithIdentifier("goToViewSeguey", sender: self)