Tzar
Tzar

Reputation: 4789

A simple notes app in Swift Playgrounds

I’m trying to create a simple notes app in Swift Playgrounds, but I’m stuck.

How can I send data from master view (which is UITableView) to the detail view (which is UITextView) and vice versa without prepareForSegue, since there are no Storyboards?

What am I missing or doing wrong?

I’ve been desparetly trying to find a solution so any help would be greatly appreciated.

Here is my playground:

import UIKit
import PlaygroundSupport

class MasterViewController: UITableViewController {

    var noteData: [String] = []
    var selectedRow: Int = -1
    var newRowLabel: String = ""

    override func viewDidLoad() {
        configureNavigationBar()
        loadNote()
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        if selectedRow == -1 {
            return
        }
        noteData[selectedRow] = newRowLabel
        if newRowLabel == "" {
            noteData.remove(at: selectedRow)
        }
        tableView.reloadData()
        saveNote()
    }

    @objc func newNote() {
        let noteTitle: String = ""
        noteData.insert(noteTitle, at: 0)
        let indexPath: IndexPath = IndexPath(row: 0, section: 0)
        tableView.insertRows(at: [indexPath], with: .automatic)
        tableView.selectRow(at: indexPath, animated: true, scrollPosition: .none)
        navigationController?.pushViewController(DetailViewController(), animated: true)
    }

    func configureNavigationBar() {
        title = "Notes"
        let addButton = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(newNote))
        navigationItem.rightBarButtonItem = addButton
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return noteData.count
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        var cell = tableView.dequeueReusableCell(withIdentifier: "Cell")
        if cell == nil {
            cell = UITableViewCell(style: .default, reuseIdentifier: "Cell")
        }
        cell?.textLabel?.text = noteData[indexPath.row]
        return cell!
    }

    override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
        noteData.remove(at: indexPath.row)
        tableView.deleteRows(at: [indexPath], with: .fade)
        saveNote()
    }

    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        navigationController?.pushViewController(DetailViewController(), animated: true)
    }

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        let detailView: DetailViewController = segue.destination as! DetailViewController
        selectedRow = tableView.indexPathForSelectedRow!.row
        detailView.masterView = self
        detailView.setText(noteText: noteData[selectedRow])
    }

    func saveNote() {
        UserDefaults.standard.set(noteData, forKey: "Notes")
        UserDefaults.standard.synchronize()
    }

    func loadNote() {
        if let loadedNote = UserDefaults.standard.value(forKey: "notes") as? [String] {
            noteData = loadedNote
            tableView.reloadData()
        }
    }
}

class DetailViewController: UIViewController {

    let textView = UITextView()
    var noteText: String = ""
    var masterView: MasterViewController!

    override func loadView() {
        configureDetailView()
    }

    override func viewDidLoad() {
        configureTextView()
    }

    func configureDetailView() {
        view = textView
    }

    func configureTextView() {
        textView.text = noteText
    }

    func setText(noteText: String) {
        if isViewLoaded {
            textView.text = noteText
        }
    }

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        masterView?.newRowLabel = noteText
    }
}

let masterViewController = MasterViewController()
let navigationController = UINavigationController(rootViewController: masterViewController)

PlaygroundPage.current.liveView = navigationController

Upvotes: 1

Views: 911

Answers (2)

cldrr
cldrr

Reputation: 1238

Instead of

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    navigationController?.pushViewController(DetailViewController(), animated: true)
}

You can create DetailViewController and assign the noteText, like this

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    let detailViewController = DetailViewController()
    detailViewController.noteText = noteData[indexPath.row]
    detailViewController.masterView = self
    navigationController?.pushViewController(detailViewController, animated: true)
}

The function prepare can be deleted, it's not used because you don't use storyboards.

And don't forget to adapt newNote():

@objc func newNote() {
    let noteTitle: String = ""
    noteData.insert(noteTitle, at: 0)
    let indexPath: IndexPath = IndexPath(row: 0, section: 0)
    tableView.insertRows(at: [indexPath], with: .automatic)
    tableView.selectRow(at: indexPath, animated: true, scrollPosition: .none)
    let detailViewController = DetailViewController()
    detailViewController.masterView = self
    navigationController?.pushViewController(detailViewController, animated: true)
}

Upvotes: 1

Azhar Ali
Azhar Ali

Reputation: 299

In your didSelectRow method:

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    navigationController?.pushViewController(DetailViewController(), animated: true)
}

you're creating your DetailViewController() but not passing any data to it when your view controller has variables to accept data, so in order to do that instead of doing:

navigationController?.pushViewController(DetailViewController(), animated: true)

you can do:

let detailVC = DetailViewController()
    detailVC.noteText = noteData[indexPath.row]
    navigationController?.pushViewController(detailVC, animated: true)

EDIT: I am updating my answer from here, because your solution needs a bunch of changes to make things, like the way i explained above how to pass data, you have to do this in various places as well where you're creating the new note also

First for your new to save and the table view cells to update itself you need adapt your newNote() function to this:

@objc func newNote() {
    let noteTitle: String = ""
    noteData.insert(noteTitle, at: 0)
    let indexPath: IndexPath = IndexPath(row: 0, section: 0)
    tableView.insertRows(at: [indexPath], with: .automatic)
    tableView.selectRow(at: indexPath, animated: true, scrollPosition: .none)
    selectedRow = indexPath.row
    let detailVC = DetailViewController()
    detailVC.masterView = self
    navigationController?.pushViewController(detailVC, animated: true)
}

where you are setting your selectedRow variable to your inserted indexPath and also passing your masterView to the your DetailsViewController()

Second you need to also change the didSelectRow, use the same solution i posted, but similarly also pass in your masterView and selectedRow there also so it should look like this:

let detailVC = DetailViewController()
    detailVC.noteText = noteData[indexPath.row]
    detailVC.masterView = self
    selectedRow = indexPath.row
    navigationController?.pushViewController(detailVC, animated: true)

Third your DetailViewController has one thing missing, whenever text is entered in your text view it is not updating noteText and your viewWillDisappear method relies on that, you need to set your text view's delegate to self and listen to text changes, however for the easier solution change your viewWillDisappear to directly access textView like so:

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    masterView?.newRowLabel = textView.text
}

Fourth when you go back from this screen, your MasterViewController needs to save this in your viewWillAppear method, some changes are needed there as well, actually not much you just need to reset your selectedRow variable once the note is saved so after you call:

saveNote()

in the next line also add:

selectedRow = -1

Upvotes: 2

Related Questions