简体   繁体   中英

Manipulating from grandchild view controller - Swift Xcode

This is my first time using swift or Xcode.
I'm trying to make a simple transaction register app

The first view has a table, in which each row represents an account and it's balance. When you click on a row, it opens up a second view, via segue, which contains a table of all transactions for that account. At the top of this view there is an 'Add Transaction' button, which opens up a third view, that has a form and an 'Add' button. When the 'Add' button is pressed, I use the .reloadData() on the table in the second view and the third view is dismissed. But, the table, visually, does not have an additional row in it. Which is because after the 3rd view closes, the newly added transaction is no longer in the transactions array.


Am I doing something wrong? My attempt and images are below.
First view

import UIKit

class AccountsViewController: UIViewController {
    
    @IBOutlet weak var newAccountNameUITextField: UITextField!
    @IBOutlet weak var newAccountBalanceUITextField: UITextField!
    @IBOutlet weak var addNewAccountUIButton: UIButton!
    @IBOutlet weak var accountsUITableView: UITableView!
    
    var selectedAccount: Account = Account(name: "", balance: "")
    var accounts = [Account(name: "PNC", balance: "45.93")]
    
    override func viewDidLoad() {
        super.viewDidLoad()
        accountsUITableView.delegate = self
        accountsUITableView.dataSource = self
    }
    
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {

        super.prepare(for: segue, sender: sender)

        if let transactionsViewController = segue.destination as? TransactionsViewController {
            transactionsViewController.modalPresentationStyle = .fullScreen
            transactionsViewController.account = selectedAccount
        }
    }
    
}

extension AccountsViewController: UITableViewDelegate {
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        selectedAccount = accounts[indexPath.row]
        performSegue(withIdentifier: "trasactionsSegue", sender: self)
    }
}

extension AccountsViewController: UITableViewDataSource {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return accounts.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "account", for: indexPath) as! AccountCell
        cell.selectionStyle = .none
        cell.nameUILabel?.text = accounts[indexPath.row].name
        cell.balanceUILabel?.text = accounts[indexPath.row].balance
        return cell
    }
}

Second View

import UIKit

class TransactionsViewController: UIViewController {
    
    @IBOutlet weak var nameUILabel: UILabel!
    @IBOutlet weak var TransactionsUITableView: UITableView!
    @IBOutlet weak var balanceUILabel: UILabel!
    
    var account: Account = Account(name: "", balance: "", transactions: [])
    
    override func viewDidLoad() {
        super.viewDidLoad()
        TransactionsUITableView.dataSource = self
        nameUILabel.text = account.name
        balanceUILabel.text = account.balance
    }
    
    //Pass data to newTransactionViewController
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        super.prepare(for: segue, sender: sender)

        if let newTransactionViewController = segue.destination as? NewTransactionViewController {
            newTransactionViewController.account = account
        }
    }
    
    //Dismiss this view when Accounts button is pressed
    @IBAction func backToAccountsTouchUpInside(_ sender: UIButton) {
        self.dismiss(animated: true, completion: {
            self.presentingViewController?.dismiss(animated: true, completion: nil)
        })
    }
    
    
    @IBAction func addTransactionTouchUpInside(_ sender: UIButton) {
        performSegue(withIdentifier: "addTransactionSegue", sender: self)
    }
    
    @IBAction func unwindToViewControllerA(segue: UIStoryboardSegue) {
        DispatchQueue.global(qos: .userInitiated).async {
            DispatchQueue.main.async {
                //At this point the newly added transaction is missing
                self.TransactionsUITableView.reloadData()
            }
        }
    }
}

extension TransactionsViewController: UITableViewDataSource {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return account.transactions.count
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "transaction", for: indexPath) as! TransactionCell
        cell.selectionStyle = .none
        cell.descriptionUILabel.text = account.transactions[indexPath.row].description
        cell.amountUILabel.text = account.transactions[indexPath.row].amount
        cell.balanceUILabel.text = account.transactions[indexPath.row].balanceAfterAmount
        return cell
    }
}

Third View

import UIKit

class NewTransactionViewController: UIViewController {
    
    @IBOutlet weak var clearedUISegmentedControl: UISegmentedControl!
    @IBOutlet weak var depositingUISegmentedControl: UISegmentedControl!
    @IBOutlet weak var descriptionUITextField: UITextField!
    @IBOutlet weak var amountUITextField: UITextField!
    @IBOutlet weak var addTransactionUIButton: UIButton!
    
    var account: Account? = nil
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }
    
    
    @IBAction func addTransactionTouchUpInside(_ sender: UIButton) {
        let depositing = depositingUISegmentedControl.selectedSegmentIndex == 0 ? true : false
        let cleared = clearedUISegmentedControl.selectedSegmentIndex == 0 ? true : false
        let description = descriptionUITextField.text
        let amount = amountUITextField.text
        let balanceAfterAmount = operationOnCurrency(depositing: depositing, amount: amount!, balance: account!.balance)
        
        let newTransaction = Transaction(depositing: depositing, amount: amount!, balanceAfterAmount: balanceAfterAmount, description: description!, cleared: cleared)
        account?.transactions.append(newTransaction)
        
        self.performSegue(withIdentifier: "backToTransactions", sender: self)
    }
    
}

func operationOnCurrency (depositing: Bool, amount: String, balance: String) -> String {
    //Return empty string for now
    return ""
}

The problem is that you are appending a new Transaction in the Account instance that was created in your NewTransactionViewController , rather than updating the data in the instance held by the TransactionsViewController or the root data source in the AccountsViewController (assuming that is the root data source). You need to pass the updated data backwards when the add button is pressed. You can create a delegate protocol to take care of this. Using your transition from NewTransactionViewController to TransactionsViewController example, first create the protocol:

protocol NewTransactionDelegate {
    func transactionAddedToAccount(account: Account)
} 

Then inside of your NewTransactionViewController you will want to create a delegate property:

class NewTransactionViewController: UIViewController {
    
    @IBOutlet weak var clearedUISegmentedControl: UISegmentedControl!
    @IBOutlet weak var depositingUISegmentedControl: UISegmentedControl!
    @IBOutlet weak var descriptionUITextField: UITextField!
    @IBOutlet weak var amountUITextField: UITextField!
    @IBOutlet weak var addTransactionUIButton: UIButton!
    
    var account: Account? = nil
    **var delegate: NewTransactionDelegate?**
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }

And inside of your addTransactionTouchUpInside method call the delegate method:

 @IBAction func addTransactionTouchUpInside(_ sender: UIButton) {
        let depositing = depositingUISegmentedControl.selectedSegmentIndex == 0 ? true : false
        let cleared = clearedUISegmentedControl.selectedSegmentIndex == 0 ? true : false
        let description = descriptionUITextField.text
        let amount = amountUITextField.text
        let balanceAfterAmount = operationOnCurrency(depositing: depositing, amount: amount!, balance: account!.balance)
        
        let newTransaction = Transaction(depositing: depositing, amount: amount!, balanceAfterAmount: balanceAfterAmount, description: description!, cleared: cleared)
        account?.transactions.append(newTransaction)
        **delegate?.transactionAddedToAccount(account: account)**
        
        self.performSegue(withIdentifier: "backToTransactions", sender: self)
    }

Now back in your TransactionsViewController you will want to conform to the NewTransactionDelegate protocol and implement the required method declared in the protocol:

class TransactionsViewController: UIViewController, NewTransactionDelegate {

func transactionAddedToAccount(account: Account) {
    self.account = account
    tableView.reloadData()
}

Then when you perform the segue to transition from TransactionsViewController to NewTransactionViewController you will want to set the destination view controller's delegate property to self:

//Pass data to newTransactionViewController
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        super.prepare(for: segue, sender: sender)

        if let newTransactionViewController = segue.destination as? NewTransactionViewController {
            **newTransactionViewController.delegate = self**
            newTransactionViewController.account = account
        }
    }

Now when the add button is tapped the delegate method is called and passed the new instance of account , which is then passed back to the previous view controller and updated.

Note that this will only update in account instance in the TransactionsViewController and you will also need to update the data for this account at the source or it will be lost when TransactionsViewController is deallocated. Pass the new account back to AccountsViewController , save to device, update database, etc.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM