Манипулирование с помощью контроллера представления внука — Swift Xcode

#ios #swift

#iOS #swift

Вопрос:

Я впервые использую swift или Xcode.
Я пытаюсь создать простое приложение для регистрации транзакций

В первом представлении есть таблица, в которой каждая строка представляет учетную запись и ее баланс. Когда вы нажимаете на строку, открывается второе представление через segue, которое содержит таблицу всех транзакций для этой учетной записи. В верхней части этого представления есть кнопка «Добавить транзакцию», которая открывает третье представление, в котором есть форма и кнопка «Добавить». При нажатии кнопки «Добавить» я использую .reloadData() для таблицы во втором представлении, а третье представление отклоняется. Но, визуально, в таблице нет дополнительной строки. Это связано с тем, что после закрытия 3-го представления недавно добавленная транзакция больше не находится в массиве транзакций.

Я делаю что-то не так? Моя попытка и изображения приведены ниже.
Первый просмотр

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

Комментарии:

1. Взгляните на эту статью: learnappmaking.com /… . Если вы прокрутите вниз, он охватывает обратную передачу данных с дочернего контроллера представления на родительский контроллер представления.

Ответ №1:

Проблема в том, что вы добавляете новый Transaction в Account экземпляр, который был создан в вашем NewTransactionViewController , вместо того, чтобы обновлять данные в экземпляре, хранящемся в TransactionsViewController или корневом источнике данных в AccountsViewController (при условии, что это корневой источник данных). Вам необходимо передать обновленные данные в обратном направлении при нажатии кнопки добавления. Вы можете создать протокол делегирования, чтобы позаботиться об этом. Используя ваш пример перехода от NewTransactionViewController к TransactionsViewController , сначала создайте протокол:

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

Затем, когда вы выполняете переход к переходу от TransactionsViewController к NewTransactionViewController , вы захотите установить для свойства делегата контроллера представления назначения значение 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 , сохраните на устройстве, обновите базу данных и т.д.