-1

I have a view controller, and inside that view controller I have a table view controller (see image here).

The program works as follows:

The user is typing someones email address, the table view is being updated as the user is typing.

Once the user is confident that the table is displaying the cell that he wants, he can click it and the info from the pressed cell "autocompletes" what the user was typing.

I have both view controllers (a table view and the regular one). I know how to send information from the viewController to the tableViewController, but I do not know how to to the reverse of that. Attached is the code for the viewController:

class PayViewController: UIViewController, STPAddCardViewControllerDelegate {
    
    
    var finalAmount = ""
    var toPay = 0.00
    var successfullPayment = false
    var payToUser = ""
    var updatedBalance = ""
    
    lazy var functions = Functions.functions()
    var resultText = "" // empty
    var input = ""
    var email = ""
    
    var sele = ""
    
    @IBOutlet weak var forField: UITextField!
    @IBOutlet weak var toField: UITextField!
    @IBOutlet weak var amountLabel: UILabel!
    
    //toField.addTarget(self, action: #selector(UsersTableViewController.textChanges(_:)), for: UIControl.Event.editingChanged)
    var myTable:UsersTableViewController?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        //var myTable:UsersTableViewController?
        amountLabel.text = "$ \(finalAmount)"
        myTable = self.children[0] as! UsersTableViewController
        self.toField.addTarget(myTable, action: #selector(UsersTableViewController.textChanges(_:)), for: UIControl.Event.editingChanged)
    }

    
    @IBAction func paraFieldEdditingStarted(_ sender: Any) {
         myTable!.filterContent(searchText:forField.text!)
        //self.toField.addTarget(, action: #selector(UsersTableViewController.textChanges(_:)), for: UIControl.Event.editingChanged)
    }

and the following is the code for the tableViewController

class UsersTableViewController: UITableViewController, UISearchResultsUpdating {
    
    func updateSearchResults(for searchController: UISearchController) {
        //update the search results
        filterContent(searchText: searchT)
    }

    
    @objc func textChanges(_ textField: UITextField) {
        let text = textField.text! // your desired text here
        // Now do whatever you want.
        searchT = text
        filterContent(searchText:textField.text!)
    }
    

    @IBOutlet var usersTableView: UITableView!
    let searchController = UISearchController(searchResultsController: nil)
    
    var searchT = ""
    var usersArray = [NSDictionary?]()
    var filteredUsers = [NSDictionary?]()
    
    var databaseRef: DatabaseReference!
    
    
    override func viewDidLoad() {
        super.viewDidLoad()
 
        searchController.searchResultsUpdater = self
        searchController.dimsBackgroundDuringPresentation = false
        definesPresentationContext = true
        
        tableView.tableHeaderView = searchController.searchBar
        
        databaseRef = Database.database().reference()
        let usersRef = databaseRef.child("users")
        let query = usersRef.queryOrdered(byChild: "email")
        query.observe(.childAdded, with: {(snapshot) in
            self.usersArray.append((snapshot.value as? NSDictionary?)!)
            
            //insert the rows
            self.usersTableView.insertRows(at: [IndexPath(row:self.usersArray.count-1, section: 0)], with: UITableView.RowAnimation.automatic)
            
            
        }) { (error) in
            print(error.localizedDescription)
        }
        
        // Uncomment the following line to preserve selection between presentations
        // self.clearsSelectionOnViewWillAppear = false

        // Uncomment the following line to display an Edit button in the navigation bar for this view controller.
        // self.navigationItem.rightBarButtonItem = self.editButtonItem
    }

    // MARK: - Table view data source

    override func numberOfSections(in tableView: UITableView) -> Int {
        // #warning Incomplete implementation, return the number of sections
        return 1
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        // #warning Incomplete implementation, return the number of rows
        
        if (self.searchT != ""){
            return filteredUsers.count
        }
        return self.usersArray.count
    }

    
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)

        let user: NSDictionary
        if ((searchT != "")){
            user = filteredUsers[indexPath.row]!
        }else{
            user = self.usersArray[indexPath.row]!
        }

        cell.textLabel?.text = user["email"] as? String
        cell.detailTextLabel?.text = user["name"] as? String
        
        return cell
    }
    
    func filterContent(searchText: String){
        
        self.filteredUsers =  self.usersArray.filter({ user in
            let userEmail = user!["email"] as? String
            return(userEmail?.lowercased().contains(searchText.lowercased()))!
        })
        
        tableView.reloadData()
    }
    
    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        //let vc = storyboard?.instantiateViewController(withIdentifier: "ConfirmationVC") as? PayViewController
        //vc?.forField.text = filteredUsers[indexPath.row]!["email"] as! String
    }
    
    func setContents(searchText: String){
       // searchT = searchText
    }

as you can see at the very end of the tableviewcontroller, I have attempted to send information back using vc. is there any easier way of doing this?

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459

2 Answers2

0

Your attempt

let vc = storyboard?.instantiateViewController(withIdentifier: "ConfirmationVC") as? PayViewController

...fails because it makes a new, different instance of PayViewController - not the instance that contains this UsersTableViewController.

The PayViewController is this UsersTableViewController's parent. You might need to cast it to a PayViewController in order to call a method or set a property in it:

let vc = self.parent as? PayViewController

(Please note that it is unusual for one view controller to set another view controller's interface directly as you are attempting to do. You should provide PayViewController with a method that UsersTableViewController can call in good order.)

matt
  • 515,959
  • 87
  • 875
  • 1,141
0

Although creating a delegate protocol and adding a delegate property to your tableViewController is a fine way to go, there's another way too.

Just add a property to your tableViewController that's a closure:

var updatedData: ((_ email: String, _ name: String)->Void)?

Your tableViewController can send information "up the chain" to an unknown parent view controller by simply calling

self.updatedData?(theEmail, theName)

In your parent view controller's viewDidLoad (or wherever you choose), have it do this:

tableViewController.updatedData = 
  { [weak self] email, name in
      // do your code here
  }

So simple. There are various pros and cons to this approach vs. a formal protocol. I highly recommend you try both approaches and get very familiar with both so that over time you'll recognize when to use which approach. This approach is informal and is often more suitable for communicating "up the chain" within an app (vs. an SDK) especially where the parent view controller isn't likely to be swapped out for another. But it's also perfectly reasonable to say you always want to formalize everything via protocols. Just a little more code that way.

I actually use this informal approach a lot. I also wrap a bunch of these closure variables into a protocol that my view model would implement, just so that I can dependency inject a different view model when I am UI-testing a single view controller independently from my app. Anyhow, that's a different situation (viewModel to viewController communication, rather than your situation of child viewController to parent viewController communication).

Smartcat
  • 2,834
  • 1
  • 13
  • 25