Search code examples
iosswiftxcodeuitableviewnsuserdefaults

How to add multiple data to a UITableView from a UITextField? (SWIFT 4)


I am trying to create a program on Xcode that allows the user to enter multiple data into a table view through a text field (when a button is clicked). When the data is added I would like it to be stored and not be deleted after the app is closed - for this part I believe that I would have to use NSUserDefaults, however, I am unsure how I would save an array of strings? (I'm only familiar with storing a single string).

This is what my view controller currently looks like.

I have not done much on my view controller at all but this is what it currently has.

import UIKit

class NewViewController: UIViewController {

@IBOutlet weak var text: UITextField!

@IBOutlet weak var tableView: UITableView!



override func viewDidLoad() {
    super.viewDidLoad()


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

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}


/*
// MARK: - Navigation

// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    // Get the new view controller using segue.destinationViewController.
    // Pass the selected object to the new view controller.
}
*/

}

Solution

  • Let's tackle this step-by-step...

    TL;DR - For your convenience, I've put the final code into a sample project on Github. Feel free to use any or all of the code in your apps. Best of luck!

    Step 1 - Conform to UITableView Protocols

    "...enter multiple data into a table view..."

    At a minimum, UITableView requires you to conform to two protocols in order to display data: UITableViewDelegate and UITableViewDataSource. Interface Builder handles the protocol declaration for you if you use the built-in UITableViewController object, but in your case you cannot use that object because you only want the UITableView to take up a portion of the view. Therefore, you must implement the protocols yourself by adding them to ViewController's signature:

    Swift 4

    class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
    
    }
    

    Step 2 - Implement UITableView Protocol Required Methods

    Now that you have the protocols declared, Xcode displays an error until three required methods are implemented inside of your ViewController class. The bare minimum implementation for these methods is:

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        
    }
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 0
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        return UITableViewCell()
    }
    

    You'll implement these methods later, but at this point your code should compile.

    Step 3 - Connect UITableView's Protocols to ViewController

    Since you are using a standard UITableView object, ViewController is not connected by default to the code you just implemented in the protocol methods. To make a connection, add these lines to viewDidLoad():

    override func viewDidLoad() {
        super.viewDidLoad()
        
        tableView.delegate = self
        tableView.dataSource = self
    }
    

    Alternatively, you could use the CONTROL + DRAG technique in Interface Builder to connect the delegate and data source from your UITableView to ViewController.

    NOTE: In this case, self refers to the ViewController since you're inside of the ViewController class.

    Step 4 - UITextField Setup

    "...through a text field..."

    You previously added an IBOutlet for your UITextField that is connected to Interface Builder, so there is nothing more to do here.

    Step 5 - IBAction for the Add Button

    (when a button is clicked)."

    You need to add an IBAction to your ViewController class and connect it to your Add Button in Interface Builder. If you prefer to write code and then connect the action, then add this method to your ViewController:

    @IBAction func addButtonPressed(_ sender: UIButton) {
    
    }
    

    If you use Interface Builder and the CONTROL + DRAG technique to connect the action, the method will be added automatically.

    Step 6 - Add an Array Property to Store Data Entries

    "...save an array of strings..."

    You need an array of strings to store the user's entries. Add a property to ViewController that is initialized as an empty array of strings:

    var dataArray = [String]()
    

    Step 7 - Finish Implementing UITableView Protocol Methods

    At this point you have everything you need to finish implementing UITableView's protocol methods. Change the code to the following:

    //1
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        //Do nothing   
    }
    
    //2
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return dataArray.count
    }
    
    //3
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
        cell.textLabel?.text = dataArray[indexPath.row]
        return cell
    }
    
    1. In the future, if you want to do something when the user taps a cell, you will want to add code to tableView(_:didSelectRowAt:).

    2. You now create the same number of rows as the number of values in dataArray.

    3. To make this work with Interface Builder, make sure you go to the Attributes Inspector for your UITableViewCell and set the Cell Identifier to Cell. Check out the documentation for more on Dequeuing Cells.

    Step 8 - Finish Implementing addButtonPressed(_:)

    As suggested in @dani's answer, in the action you need to implement code that appends the user's text to the array, but only if the text is not blank or empty. It is also a good idea to check if dataArray already contains the value you entered using dataArray.contains, depending on what you want to accomplish:

    if textField.text != "" && textField.text != nil {
        let entry = textField.text!
        if !dataArray.contains(entry) {
            dataArray.append(entry)
            textField.text = ""
        }
        tableView.reloadData()
    }
    

    Step 9 - Persist Data with UserDefaults

    "When the data is added I would like it to be stored and not be deleted after the app is closed."

    To save dataArray to UserDefaults, add this line of code after the line that appends an entry inside of the addButtonPressed(_:) action:

    UserDefaults.standard.set(dataArray, forKey: "DataArray")
    

    To load dataArray from UserDefaults, add these lines of code to viewDidLoad() after the call to super:

    if let data = UserDefaults.standard.value(forKey: "DataArray") as? [String] {
        dataArray = data
    }