Reputation: 4789
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
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
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