How do I get a button to show a different view controller only under a specific condition?

How do I get a button to show a different view controller only under a specific condition?

If the condition is not met, I want it to do nothing. I created a segue between the button and the view controller by control dragging the button to the view controller and then clicking show. When I click on the button, the button always shows the other view controller no matter what the condition is. What am I doing wrong?


This is what my code looks like.


@IBAction func btnPayoutStructure(_ sender: UIButton) {
        
        if txtViewLabels.text != nil && txtViewResults.text != nil {
            performSegue(withIdentifier: "payout structure", sender: self)
        }
        else {
            return
        }
    }



Also, how do I pass data to the other view controller from the view controller with the button?



Thank you

Accepted Reply

As was said before, you should not use


    var passedData = ViewController()


But instead pass a struct

var passedData: DataToPass?


with all the relevant data: probably containing

struct DataToPass {
     var dblCost : Double
     var dblDownPayment: Double
     // etc
}


Then, as I described in other post

In ViewController, set a var


var valueToPass : DataToPass = DataToPass(dblCost: 0.0, dblDownPayment: 0.0) // and all other elements of struct


else if txtCost.text != nil && txtDownPayment.text != nil && txtMonths.text != nil && txtAPR.text != nil {

     // Do all computations and at the end, set valueToPass
     valueToPass.dblCost = dblCost
     valueToPass.dblDownPayment = dblDownPayment
//     etc
}

pass the data in prepare


   override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "SegueToSecond" {     // Should test it is the right segue identifier
            if let destVC = segue.destination as? SecondViewController {
                    destVC.passedData = valueToPass
            }
     }

Replies

I created a segue between the button and the view controller by control dragging the button to the view controller

What am I doing wrong?


When you want to control the segue programmatically, you need create one from the current view controller to the next view controller,

not from the button.


Also, how do I pass data to the other view controller from the view controller with the button?


You add some properties to receive the data to the other view controller, and use them in a proper method, for example `viewWillAppear()`.

And override the method `prepare(for:)` in the current view controller, in it set the next view controller's properties with the data.

You could also use remove the IBAction, keep the segue from the button and call ShouldPerformSegue,


    override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {

        return txtViewLabels.text != nil && txtViewResults.text != nil
    }


Take care of the test txtViewLabels.text != nil


What do you want to test ? That textField is not empty ?

If so, you should test


txtViewLabels.text != nil && !txtViewLabels.text!.isEmpty && txtViewResults.text != nil && !txtViewResults.text!.isEmpty

This is my code trying to pass data from one view controller to another view controller. I created a new file. What am I doing wrong?


import UIKit

class PayoutStructure: UIViewController, UITableViewDataSource, UITableViewDelegate {
    
    var passedData = ViewController()
    
    var data: [String] = []
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return data.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
        cell.textLabel?.text = data[indexPath.row]
        return cell
    }
    
    @IBOutlet weak var table: UITableView!
    

    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.
    }

Still editing? I cannot find any code which passes data from one view controller to another view controller.

When you want to describe passing, you should better show both sides. From and to. Please show both view controllers.


But this line:

    var passedData = ViewController()


Generally, instantiating a view controller in this way does not work as expected in most cases.

Very often, you pass data to the controller you are segueing to in the prepare(for segue) of the first controller.


You set var of the destination controller that you then use in the viewDidload of this second controller.


    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "SegueToSecond" {     // Should test it is the right segue identifier
            if let destVC = segue.destination as? SecondViewController {
                    destVC.property = someValueToPass
            }
     }

My app is a loan calculator that calculates 1. Monthly Payment, 2. Total interest, 3. Total amount.

There is another button that after clicked I want it to show the second view controller with a table view of the payout structure for each month.

I can do this with a For in loop in the same view controller but I want it to display the payout structure in the second view controller.

I know that I will have to write a function with a For in loop and put it in viewDidLoad() to get the table to load when this view controller loads.

I need to pass the result data to the second view controller to get this to happen.


This is my first view controllers code that I want to pass to the second view controller. The first view controllers class is named ViewController. The second view controllers class is named PayoutStructure.


else if txtCost.text != nil && txtDownPayment.text != nil && txtMonths.text != nil && txtAPR.text != nil {
            // Clear the message.
            lblMessage.text = ""
            
            dblCost = Double(txtCost.text!)!
            dblDownPayment = Double(txtDownPayment.text!)!
            intMonths = Int(txtMonths.text!)!
            dblAPR = Double(txtAPR.text!)!
            
            // Get the tax rate.
            dblTaxRate = dblTaxRate / 100
            
            // Get the APR rate.
            dblAPR = dblAPR / 100
            
            // Get the monthly rate.
            dblMonthlyRate = dblAPR / dblMONTHS_YEAR
            
            // Get the total tax.
            dblTotalTax = (dblCost) * (dblTaxRate)
            
            // Get the monthly payment.
            dblMonthlyPayment = ((dblCost + dblTotalTax) - dblDownPayment) * (dblMonthlyRate / (1 - pow(1 + dblMonthlyRate, Double(-intMonths))))
            
            // Get the amount of the loan.
            dblLoan = dblCost + dblTotalTax - dblDownPayment
            
            // Get the total interest.
            dblTotalInterest = dblMonthlyPayment * Double(intMonths) - dblLoan
            
            // Get the Total Amount.
            dblTotalAmount = dblCost + dblTotalInterest + dblTotalTax
            
            // Get the Vehicle Cost.
            dblCost = ((dblTotalAmount - dblTotalInterest) - dblTotalTax)
            
            // Format the results.
            let fCost = NSNumber(value: dblCost)
            let fResultCost = NumberFormatter.localizedString(from: fCost, number: .currency)
            
            let fDownPayment = NSNumber(value: dblDownPayment)
            let fResultDownPayment = NumberFormatter.localizedString(from: fDownPayment, number: .currency)
            
            let fLoan = NSNumber(value: dblLoan)
            let fResultLoan = NumberFormatter.localizedString(from: fLoan, number: .currency)
            
            let fMonthlyPayment = NSNumber(value: dblMonthlyPayment)
            let fResultMonthkyPayment = NumberFormatter.localizedString(from: fMonthlyPayment, number: .currency)
            
            let fAPR = NSNumber(value: dblAPR)
            let fResultAPR = NumberFormatter.localizedString(from: fAPR, number: .percent)
            
            let ftaxRate = NSNumber(value: dblTaxRate)
            let fResultTaxRate = NumberFormatter.localizedString(from: ftaxRate, number: .percent)
            
            let fTotalInterest = NSNumber(value: dblTotalInterest)
            let fResultTotalInterest = NumberFormatter.localizedString(from: fTotalInterest, number: .currency)
            
            let fTotalTax = NSNumber(value: dblTotalTax)
            let fResultTotalTax = NumberFormatter.localizedString(from: fTotalTax, number: .currency)
            
            let fTotalAmount = NSNumber(value: dblTotalAmount)
            let fResultTotalAmount = NumberFormatter.localizedString(from: fTotalAmount, number: .currency)
            
            txtViewLabels.text = " APR:\n Tax%:\n Months:\n Cost:\n Total Tax:\n Down Payment:\n Loan Amount\n Monthly Payment:\n Total Interest:\n Total Amount:\n"
            
            txtViewResults.text = " \(fResultAPR)\n \(fResultTaxRate)\n \(intMonths)\n \(fResultCost)\n \(fResultTotalTax)\n \(fResultDownPayment)\n \(fResultLoan)\n \(fResultMonthkyPayment)\n \(fResultTotalInterest)\n \(fResultTotalAmount)"
            
        }
    }

My second view controllers code is:


class PayoutStructure: UIViewController, UITableViewDataSource, UITableViewDelegate {
    
    var passedData = ViewController()
    var data: [String] = []
    
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return data.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
        cell.textLabel?.text = data[indexPath.row]
        return cell
    }
    
    @IBOutlet weak var table: UITableView!
    

    override func viewDidLoad() {
        super.viewDidLoad()
        

        // Do any additional setup after loading the view.
    }

As was said before, you should not use


    var passedData = ViewController()


But instead pass a struct

var passedData: DataToPass?


with all the relevant data: probably containing

struct DataToPass {
     var dblCost : Double
     var dblDownPayment: Double
     // etc
}


Then, as I described in other post

In ViewController, set a var


var valueToPass : DataToPass = DataToPass(dblCost: 0.0, dblDownPayment: 0.0) // and all other elements of struct


else if txtCost.text != nil && txtDownPayment.text != nil && txtMonths.text != nil && txtAPR.text != nil {

     // Do all computations and at the end, set valueToPass
     valueToPass.dblCost = dblCost
     valueToPass.dblDownPayment = dblDownPayment
//     etc
}

pass the data in prepare


   override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "SegueToSecond" {     // Should test it is the right segue identifier
            if let destVC = segue.destination as? SecondViewController {
                    destVC.passedData = valueToPass
            }
     }

I did this and I think I am still doing something wrong. This is the code from my first view controller.


struct DataToPass {
    var principal: Double = 0
    var balance: Double = 0
    var monthlyInterest: Double = 0
    var paymentNumber: Int = 0
}

class ViewController: UIViewController, UITextFieldDelegate, UITextViewDelegate {
    
    
    var valueToPass: DataToPass = DataToPass(principal: 0.00, balance: 0.00, monthlyInterest: 0.00, paymentNumber: 0)
    
    // Class-level constant to hold the months per year.
    let dblMONTHS_YEAR: Double = 12
    
    // To not allow more than 1 decimal point.
    func textField(_ textField: UITextField,shouldChangeCharactersIn range: NSRange,replacementString string: String) -> Bool
    {
        let countdots = (textField.text?.components(separatedBy: ".").count)! - 1
        
        if countdots > 0 && string == "."
        {
            return false
        }
        return true
    }
    @IBAction func textFieldCost(_ sender: TextField) {
        func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
            self.view.endEditing(true)
        }
    }
    @IBAction func textFieldDownPayment(_ sender: TextField) {
        func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
            self.view.endEditing(true)
        }
    }
    @IBAction func textFieldMonths(_ sender: TextField) {
        func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
            self.view.endEditing(true)
        }
    }
    @IBAction func textFieldAPR(_ sender: TextField) {
        func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
            self.view.endEditing(true)
        }
    }
    
    @IBOutlet weak var txtViewLabels: UITextView!
    @IBOutlet weak var txtViewResults: UITextView!
    
    // Text Field Outlets.
    @IBOutlet weak var txtCost: UITextField!
    @IBOutlet weak var txtDownPayment: UITextField!
    @IBOutlet weak var txtMonths: UITextField!
    @IBOutlet weak var txtAPR: UITextField!
    
    // Error Message.
    @IBOutlet weak var lblMessage: UILabel!
    
    // Buttons
    @IBAction func btnCalculate(_ sender: UIButton) {
        
        var dblAPR: Double = 0                         // To hold the Annual Rate.
        var dblTotalAmount: Double = 0                 // To hold the vehicle total cost.
        var dblCost: Double = 0                        // To hold vehicle cost.
        var dblDownPayment: Double = 0                 // To hold down payment.
        var intMonths: Int = 0                         // To hold number of months for the loan.
        var dblLoan: Double = 0                        // To hold the amount of the loan.
        var dblMonthlyPayment: Double = 0              // To hold the monthly payment.
        var dblTotalInterest: Double = 0               // To hold the total interest.
        var dblMonthlyRate: Double = 0                 // To hold the monthly Rate.
        
        var monthlyInterest: Double = 0
        var balance: Double = 0
        var principal: Double = 0
        var paymentNumber: Int = 0
        
        var data: [String] = []
        
        func passedValue() {
            
            func paymentStructure() {
                
                data = []
                
                balance = dblLoan
                
                for _ in 1...intMonths {
                    paymentNumber+=1
                
                    dblCost = Double(txtCost.text!)!
                    dblDownPayment = Double(txtDownPayment.text!)!
                    intMonths = Int(txtMonths.text!)!
                    dblAPR = Double(txtAPR.text!)!
                    
                    monthlyInterest = balance * ((dblAPR/100)/12)
                    principal = dblMonthlyPayment - monthlyInterest
                    balance = balance - principal
                    
                    let fPrincipal = NSNumber(value: principal)
                    let fResultPrincipal = NumberFormatter.localizedString(from: fPrincipal, number: .currency)
                    
                    let fBalance = NSNumber(value: balance)
                    let fResultBalance = NumberFormatter.localizedString(from: fBalance, number: .currency)
                    
                    let fMonthlyInterest = NSNumber(value: monthlyInterest)
                    let fResultMonthlyInterest = NumberFormatter.localizedString(from: fMonthlyInterest, number: .currency)
                    
                    data += ["\(paymentNumber). P: \(fResultPrincipal), I: \(fResultMonthlyInterest), B: \(fResultBalance)"]
                }
                valueToPass.paymentNumber = intMonths
                valueToPass.monthlyInterest = monthlyInterest
                valueToPass.principal = principal
                valueToPass.balance = balance
            }
        }
else if txtCost.text != nil && txtDownPayment.text != nil && txtMonths.text != nil && txtAPR.text != nil {
            // Clear the message.
            lblMessage.text = ""
            
            dblCost = Double(txtCost.text!)!
            dblDownPayment = Double(txtDownPayment.text!)!
            intMonths = Int(txtMonths.text!)!
            dblAPR = Double(txtAPR.text!)!
            
            // Get the APR rate.
            dblAPR = dblAPR / 100
            
            // Get the monthly rate.
            dblMonthlyRate = dblAPR / dblMONTHS_YEAR
            
            // Get the monthly payment.
            dblMonthlyPayment = (dblCost - dblDownPayment) * (dblMonthlyRate / (1 - pow(1 + dblMonthlyRate, Double(-intMonths))))
            
            // Get the amount of the loan.
            dblLoan = dblCost - dblDownPayment
            
            // Get the total interest.
            dblTotalInterest = dblMonthlyPayment * Double(intMonths) - dblLoan
            
            // Get the Total Amount.
            dblTotalAmount = dblCost + dblTotalInterest
            
            // Get the Vehicle Cost.
            dblCost = dblTotalAmount - dblTotalInterest
            
            // Format the results.
            let fCost = NSNumber(value: dblCost)
            let fResultCost = NumberFormatter.localizedString(from: fCost, number: .currency)
            
            let fDownPayment = NSNumber(value: dblDownPayment)
            let fResultDownPayment = NumberFormatter.localizedString(from: fDownPayment, number: .currency)
            
            let fLoan = NSNumber(value: dblLoan)
            let fResultLoan = NumberFormatter.localizedString(from: fLoan, number: .currency)
            
            let fMonthlyPayment = NSNumber(value: dblMonthlyPayment)
            let fResultMonthkyPayment = NumberFormatter.localizedString(from: fMonthlyPayment, number: .currency)
            
            let fAPR = NSNumber(value: dblAPR)
            let fResultAPR = NumberFormatter.localizedString(from: fAPR, number: .percent)
            
            let fTotalInterest = NSNumber(value: dblTotalInterest)
            let fResultTotalInterest = NumberFormatter.localizedString(from: fTotalInterest, number: .currency)
            
            let fTotalAmount = NSNumber(value: dblTotalAmount)
            let fResultTotalAmount = NumberFormatter.localizedString(from: fTotalAmount, number: .currency)
            
            txtViewLabels.text = " APR:\n Months: \n Cost:\n Down Payment:\n Loan Amount:\n Monthly Payment:\n Total Interest:\n Total Amo
      


            txtViewResults.text = " \(fResultAPR)\n \(intMonths)\n \(fResultCost)\n \(fResultDownPayment)\n \(fResultLoan)\n \(fResultMonthkyPayment)\n \(fResultTotalInterest)\n \(fResultTotalAmount)"
            
            passedValue()
        }
    }
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "payout structure" {
            if let destVC = segue.destination as? PayoutStructure {
                destVC.passedData = valueToPass
            }
        }
    }
}

I am getting an error on line 181. It says


Cannot assign value of type 'DataToPass' to type 'ViewController'

>I think I am still doing something wrong.


Marking the thread solved would indicate something different from that conclusion... Unless you've moved to a different problem, in which case you may want to start a new thread where it can receive the unique attention it deserves and other aren't confused by this one being closed, etc.