首页 > 解决方案 > 从孙子视图控制器进行操作 - Swift Xcode

问题描述

这是我第一次使用 swift 或 Xcode。
我正在尝试制作一个简单的交易注册应用程序

第一个视图有一个表格,其中每一行代表一个帐户,它是余额。当您单击一行时,它会通过 segue 打开第二个视图,其中包含该帐户所有交易的表格。在这个视图的顶部有一个“添加事务”按钮,它打开了第三个视图,它有一个表单和一个“添加”按钮。当按下“添加”按钮时,我在第二个视图中的表上使用 .reloadData() 并关闭第三个视图。但是,从视觉上看,该表中没有额外的行。这是因为在第三个视图关闭后,新添加的事务不再在事务数组中。


难道我做错了什么?我的尝试和图像如下。
第一视角

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
    }
}

第二视图

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
    }
}

第三视图

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 ""
}

标签: iosswift

解决方案


问题是您在 中创建TransactionAccount实例中追加一个新的,而不是更新由或 中的根数据源NewTransactionViewController持有的实例中的数据(假设是根数据源)。按下添加按钮时,您需要向后传递更新的数据。您可以创建一个委托协议来处理这个问题。使用从到示例的转换,首先创建协议:TransactionsViewControllerAccountsViewControllerNewTransactionViewControllerTransactionsViewController

protocol NewTransactionDelegate {
    func transactionAddedToAccount(account: Account)
} 

然后在您的内部,您NewTransactionViewController将要创建一个委托属性:

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()
    }

在您的addTransactionTouchUpInside方法内部调用委托方法:

 @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)
    }

现在回到你,TransactionsViewController你会想要遵守NewTransactionDelegate协议并实现协议中声明的所需方法:

class TransactionsViewController: UIViewController, NewTransactionDelegate {

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

然后,当您执行 segue 以从 to 转换时TransactionsViewControllerNewTransactionViewController您需要将目标视图控制器的委托属性设置为 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
        }
    }

现在,当点击添加按钮时,会调用委托方法并传递 的新实例account,然后将其传递回前一个视图控制器并更新。

请注意,这只会在帐户实例中更新,TransactionsViewController并且您还需要在源更新此帐户的数据,否则在解除分配时它将丢失TransactionsViewController。将新帐户传回AccountsViewController,保存到设备,更新数据库等。


推荐阅读