Dan L Dan L - 2 years ago 119
Swift Question

Getting Data From all TableViewCells

I am creating a flashcards app and right now I am creating the interface where the user creates the flashcards. I originally had it set up where the user could not add another card (add another UITableViewCell) until the current one had a term and definition. If the card was filled in, I would save the term and definition to a dictionary. What I realized part of the way through is that the user can go back to a previous cell and change the data in the cell and it would never be saved. My latest idea is to wait until the user finishes filling in all of the cells and then save the data. I can't seem to find a way to get the data of all of the cells.

What I have now in my tableViewController:

// MARK: Variables

var cards = [Int]()

var cardData = [String : String]()

// MARK: Actions

@IBAction func plusBarButtonItemPressed(sender: AnyObject) {

self.cards.append(self.cards.count + 1)

let insertionIndexPath = NSIndexPath(forRow: self.cards.count - 1, inSection: 0)

tableView.insertRowsAtIndexPaths([insertionIndexPath], withRowAnimation: .Automatic)


@IBAction func doneBarButtonItemPressed(sender: AnyObject) {

// Save each cell into cardData [termTextView.text : definitionTextView.text]


// MARK: Functions

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

return self.cards.count

And here is what I have now in my custom tableViewCell

// MARK: Outlets

@IBOutlet var termTextView: UITextView!

@IBOutlet var definitionTextView: UITextView!

Again, my question: How can I get the text inside of the textViews inside all of the tableViewCells into a dictionary when a button (save) is pressed?

Thanks for the help!

Answer Source

A tableView should not store data. Data should be kept in your model (a data structure or data structures which hold your data independent of the tableView). You should have this so that when cells scroll off of the screen and then back on, you can restore the data that is in them.

When the user enters data into a cell, you should immediately update your model.

When the save button is pressed, you should save your data from your model to a more permanent location (a file, Core Data, SQL database).

First, let's define a protocol for a CardHandler:

protocol CardHandler {
    func totalCards() -> Int
    func writeTerm(term: String, forCard cardNum: Int)
    func writeDefinition(definition: String, forCard cardNum: Int)
    func readTermForCard(cardNum: Int) -> String?
    func readDefinitionForCard(cardNum: Int) -> String?
    func addCard() -> Int
    func removeCardNum(cardNum: Int)

Next, define a class to be your model:

class Cards: CardHandler {
    struct CardRecord {
        var term: String = ""
        var definition: String = ""

    var cards = [CardRecord]()

    func totalCards() -> Int {
        return cards.count

    func writeTerm(term: String, forCard cardNum: Int) {
        if cardNum < cards.count {
            cards[cardNum].term = term

    func writeDefinition(definition: String, forCard cardNum: Int) {
        if cardNum < cards.count {
            cards[cardNum].definition = definition

    func readTermForCard(cardNum: Int) -> String? {
        if cardNum < cards.count {
            return cards[cardNum].term
        return nil

    func readDefinitionForCard(cardNum: Int) -> String? {
        if cardNum < cards.count {
            return cards[cardNum].definition
        return nil

    func addCard() -> Int {
        return cards.count - 1

    func removeCardNum(cardNum: Int) {
        if cardNum < cards.count {

In order to be notified when the user has modified a textField, make your CustomTableViewCell class be a UITextViewDelegate:

class CustomTableViewCell: UITableViewCell, UITextViewDelegate {

    @IBOutlet weak var termTextView: UITextView!
    @IBOutlet weak var definitionTextView: UITextView
    weak var cardHandler: CardHandler?

    var rowInTable = 0   // set this in cellForRowAtIndexPath to indexPath.row

    override func awakeFromNib() {

        termTextView.delegate = self
        definitionTextView.delegate = self

    func textViewDidEndEditing(textView: UITextView) {
        let text = textView.text

        if textView == termTextView {
            cardHandler?.writeTerm(text, forCard: rowInTable)
        } else if textView == definitionTextView {
            cardHandler?.writeDefinition(text, forCard: rowInTable)

In your UITableViewController, have a property to contain your model:

var cards = Cards()

In cellForRowAtIndexPath, assign cards as the cardHandler for the cell:

let cell = dequeReusableCell ... as CustomTableViewCell
cell.rowInTable = indexPath.row
cell.cardHandler = cards
cell.contentView.termTextView.text = cards.readTermForCard(indexPath.row) ?? ""
cell.contentView.defintionTextView.text = cards.readDefinitionForCard(indexPath.row) ?? ""

For numberOfRowsInSection:

return cards.totalCards()

For plusBarButtonItemPressed:

@IBAction func plusBarButtonItemPressed(sender: AnyObject) {
    let newCardNum = cards.addCard()
    let insertionIndexPath = NSIndexPath(forRow: newCardNum, inSection: 0)
    tableView.insertRowsAtIndexPaths([insertionIndexPath], withRowAnimation: .Automatic)
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download