Cocoa: Get Notified after Text Cell (NSTextField) is Edited & Start Editing Text Cell after Adding it in NSTableView in Swift 4? - swift

I have made a simple demo using TableView here: https://github.com/deadcoder0904/TableViewDemo
I have used Defaults module as a dependency
My project looks like
All the code is in ViewController.swift as follows -
import Cocoa
import Defaults
extension Defaults.Keys {
static let dreams = Defaults.Key<Array<String>>("dreams", default: [
"Hit the gym",
"Run daily",
"Become a millionaire",
"Become a better programmer",
"Achieve your dreams"
])
}
class ViewController: NSViewController, NSTableViewDataSource, NSTableViewDelegate {
#IBOutlet weak var table: NSTableView!
var dreams = defaults[.dreams]
var selectedRow:Int = 0
override func viewDidLoad() {
super.viewDidLoad()
table.dataSource = self
table.delegate = self
}
override var acceptsFirstResponder : Bool {
return true
}
override func keyDown(with theEvent: NSEvent) {
if theEvent.keyCode == 51 {
removeDream()
}
}
func tableViewSelectionDidChange(_ notification: Notification) {
let table = notification.object as! NSTableView
selectedRow = table.selectedRow
}
func numberOfRows(in tableView: NSTableView) -> Int {
return dreams.count
}
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
let dream = table.makeView(withIdentifier: tableColumn!.identifier, owner: self) as! NSTableCellView
dream.textField?.stringValue = dreams[row]
return dream
}
#IBAction func addTableRow(_ sender: Any) {
addNewDream()
}
#IBAction func removeTableRow(_ sender: Any) {
removeDream()
}
func addNewDream() {
dreams.append("Double Click or Press Enter to Add Item")
table.beginUpdates()
let last = dreams.count - 1
table.insertRows(at: IndexSet(integer: last), withAnimation: .effectFade)
table.scrollRowToVisible(last)
table.selectRowIndexes([last], byExtendingSelection: false)
table.endUpdates()
saveDreams()
}
func removeDream() {
if selectedRow >= dreams.count {
selectedRow = dreams.count - 1
}
if selectedRow != -1 {
dreams.remove(at: selectedRow)
table.removeRows(at: IndexSet(integer: selectedRow), withAnimation: .effectFade)
}
saveDreams()
}
func saveDreams() {
defaults[.dreams] = dreams
}
}
I want to do 2 things -
Get notified after Text Cell is edited so that I can save the changed data using Defaults module
After adding new Data by Clicking on the plus sign it adds Double Click or Press Enter to Add Item but what I want is I want to add Empty String which I can do with "" but I also want it to be focused & be editable so user can start entering text in it without having to Double Click or Press Enter.
I also want a solution in Swift 4 & not Objective-C. How to achieve this?

Use Cocoa Bindings, it's very powerful and saves a lot of boilerplate code.
Short tutorial:
Edit: To take full advantage of KVC the data source must be an NSObject subclass with dynamic properties
Create a simple class Dream (the description property is optional)
class Dream : NSObject {
#objc dynamic var name : String
init(name : String) { self.name = name }
override var description : String { return "Dream " + name }
}
In the view controller declare the data source array
var dreams = [Dream]()
and replace var selectedRow:Int = 0 with
#objc dynamic var selectedIndexes = IndexSet()
Go to Interface Builder
Select the table view, press ⌥⌘7 to go to the Bindings Inspector.
Bind Selection Indexes to View Controller Model Key Path selectedIndexes.
Press ⌥⌘6 and connect the dataSource (by drag&drop) to the view controller () .
Select the text field File 1 in Table Cell View in the table column. The easiest way is to ⌃⇧click in the text field area.
Press ⌥⌘7 and bind Value to Table Cell View Model Key Path objectValue.name (!)
In the view controller populate the data source array in viewDidLoad ( I don't know that framework so I leave it out) and reload the table view.
override func viewDidLoad() {
super.viewDidLoad()
let dreamNames = ["Hit the gym", "Run daily", "Become a millionaire", "Become a better programmer", "Achieve your dreams"]
dreams = dreamNames.map{Dream(name: $0)}
table.reloadData()
}
Delete acceptsFirstResponder
Delete tableViewSelectionDidChange
Delete tableView:viewFor:row:
Add
func tableView(_ tableView: NSTableView, objectValueFor tableColumn: NSTableColumn?, row: Int) -> Any? {
return dreams[row]
}
Replace addNewDream with
func addNewDream() {
let last = dreams.count
dreams.append(Dream(name: "Double Click or Press Enter to Add Item"))
table.insertRows(at: IndexSet(integer: last), withAnimation: .effectGap)
table.scrollRowToVisible(last)
table.selectRowIndexes([last], byExtendingSelection: false)
saveDreams()
}
Replace removeDream() with
func removeDream() {
guard let selectedRow = selectedIndexes.first else { return }
dreams.remove(at: selectedRow)
table.removeRows(at: IndexSet(integer: selectedRow), withAnimation: .effectFade)
saveDreams()
}
To save the array when the text was edited afterwards you have to implement the delegate method controlTextDidEndEditing(_:)
override func controlTextDidEndEditing(_ obj: Notification) {
saveDreams()
}
and in Interface Builder connect the delegate of the text field in the table view to the view controller.

Related

How to add additional textfields by clicking button in table view

I am trying to add an option to add additional student fields inside table so that user can add more than one student name.
But I am confused how to do it using table view.
I am not interested in hiding view with specific number of fields.
class ViewController: UIViewController,UITableViewDelegate,UITableViewDataSource {
struct listItems{
var title : String
var isExpandable:Bool
var maxFields :Int
init(title:String,isExp:Bool,mxF:Int) {
self.title = title
self.isExpandable = isExp
self.maxFields = mxF
}
}
#IBOutlet weak var tblListTable: UITableView!
let data : [listItems] = [listItems(title: "Name", isExp: false, mxF: 1), listItems(title: "Student Name", isExp: true, mxF: 20), listItems(title: "Email", isExp: false, mxF: 1)]
override func viewDidLoad() {
super.viewDidLoad()
tblListTable.delegate = self
tblListTable.dataSource = self
self.tblListTable.reloadData()
print("isLoaded")
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return data.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
print("cellForRow")
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! ListCell
cell.lblName.text = data[indexPath.row].title
if data[indexPath.row].isExpandable == true {
cell.btnAddField.isHidden = false
print("ishidden")
}
else {
cell.btnAddField.isHidden = true
}
return cell
}
}
List Cell Class
import UIKit
protocol AddFieldDelegate : class {
func addField( _ tag : Int)
}
class ListCell: UITableViewCell {
#IBOutlet weak var btnAddField: UIButton!
#IBOutlet weak var lblName: UILabel!
#IBOutlet weak var txtField: UITextField!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
func addField( _ tag : Int){
}
}
You are on the right track creating the AddFieldDelegate. However, rather than implementing the method inside the ListCell class you need to implement it in the ViewController.
First, change the view controller class definition line to:
class ViewController: UIViewController,UITableViewDelegate,UITableViewDataSource, AddFieldDelegate {
This will allow you to call the delegate method from the view controller. Next, when you are creating your table view cells add the line:
cell.delegate = self
After that, move the method definition of the method addField to the view controller.
So inside of your view controller add:
func addField(titleOfTextFieldToAdd: String, numberAssociatedWithTextFieldToAdd: Int) {
data.append(listItems(title: titleOfTextFieldToAdd, isExp: false, mxF: numberAssociatedWithTextFieldToAdd))
self.tableView.reloadData()
}
I used an example definition of the addField method but you can change it to anything that you would like, just make sure that you change the data array and reload the table view data.
Lastly, we must define the delegate in the ListCell class. So add this line to the ListCell class:
weak var delegate: MyCustomCellDelegate?
You can then add the text field by running the following anywhere in your ListCell class:
delegate?.addField(titleOfTextFieldToAdd: "a name", numberAssociatedWithTextFieldToAdd: 50)
For more information on delegation, look at the answer to this question.
You have to append another item in your data array on button click and reload the tableview.

Swift - Get row index with checkbox in NSTableView [duplicate]

This question already has answers here:
Get button's row in view based table
(5 answers)
Closed 4 years ago.
I'm learning Cocoa in Swift. I created a NSTableView with Viewbased.
Simple tableview
I also connected the checkbox action to ViewController. But when I clicked the checkbox, it printed -1 instead of the row index. I have to select the row first then click the checkbox to get the right index number. Are there anyway to get the row index with every single checkbox or button on each row? Here is my code:
import Cocoa
let data: [String] = ["Apple", "Microsoft", "IBM", "Tesla", "SpaceX",
"Boeing" , "Nasa"]
class ViewController: NSViewController, NSTableViewDelegate,
NSTableViewDataSource {
override func viewDidLoad() {
super.viewDidLoad()
self.table.delegate = self
self.table.dataSource = self
self.table.reloadData()
// Do any additional setup after loading the view.
}
#IBOutlet weak var table: NSTableView!
#IBAction func CheckClicked(_ sender: Any) {
print(self.table.selectedRow)
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
func numberOfRows(in tableView: NSTableView) -> Int {
return data.count
}
func tableView(_ tableView: NSTableView, viewFor tableColumn:
NSTableColumn?, row: Int) -> NSView? {
if (tableColumn?.identifier)!.rawValue == "NameColumn"
{
if let cell = tableView.makeView(withIdentifier:
NSUserInterfaceItemIdentifier(rawValue: "NameColumn"), owner: self)
as? NSTableCellView
{
cell.textField?.stringValue = data[row]
return cell
}
}
else if (tableColumn?.identifier)!.rawValue == "CheckColumn"
{
if let cell = tableView.makeView(withIdentifier:
NSUserInterfaceItemIdentifier(rawValue: "CheckColumn"), owner: self)
as? NSButton
{
return cell
}
}
return nil
}
func tableViewSelectionDidChange(_ notification: Notification) {
print(self.table.selectedRow)
}
}
This is what you are looking for, but a better implementation would be to use the action with a NSTableCellView subclass.
#IBAction func CheckClicked(_ sender: NSButton) {
// print(self.table.selectedRow)
let row = table.row(for: sender)
print("Button row \(row)")
}
I can only create a subclass based on NSButton.
class myCustomView: NSButton{
#IBOutlet weak var CheckButton: NSButtonCell!
}
Although I can't change the title of these button cell.
if (tableColumn?.identifier)!.rawValue == "CheckColumn"
{
if let cell = tableView.makeView(withIdentifier:
NSUserInterfaceItemIdentifier(rawValue: "CheckColumn"), owner: self)
as? myCustomView
{
cell.CheckButton.title = data[row]
return cell
}
}
I don't know why Xcode doens't let me create a subclass based on NSTableCellView.

Managing Multiple UItextFields

Novice coder and I'm stuck. I have 2 UITextField on my Viewcontroller that I want passed to a UITableView when "save" is tapped. I've set both textFields to delegate.self, the problem is that the data entered in the textfields only shows up on the UITableView IF I remove 1 of the UItextFields.
I'm thinking in order to use 2 (or more) UITextFields on the same VC I must need a way besides the outlets to differentiate between them. I've seen responses about tags but I don't understand.
import UIKit
class BudgetViewController: UIViewController, UITextFieldDelegate, UINavigationControllerDelegate {
// Properties:
#IBOutlet weak var datePicker: UIDatePicker!
#IBOutlet weak var nameTextField: UITextField!
#IBOutlet weak var amountTextField: UITextField!
#IBOutlet weak var dateDisplay: UILabel!
#IBOutlet weak var saveButton: UIBarButtonItem!
var budget: Budget?
// Date picker:
let dateFormatter = NSDateFormatter()
func setDate() {
dateFormatter.dateStyle = NSDateFormatterStyle.MediumStyle
dateDisplay.text = dateFormatter.stringFromDate(datePicker.date)
}
// Navigation
// This method lets you configure a view controller before it's presented
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if saveButton === sender {
let name = nameTextField.text ?? ""
let date = dateDisplay.text ?? ""
let amount = amountTextField.text ?? ""
// set the budget to be passed to the Controller, this code configures the meal prperty with the appropriate values before the segue executes
budget = Budget(date: date, name: name, amount: amount)
}
}
// Actions:
#IBAction func datePickerChanger(sender: AnyObject) {
setDate()
}
override func viewDidLoad() {
super.viewDidLoad()
// Handle the text field
nameTextField.delegate = self
amountTextField.delegate = self
}
// UITextFieldDelegate
func textFieldShouldReturn(textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
func textFieldDidEndEditing(textField: UITextField) {
}
}
import UIKit
class BudgetTableViewController: UITableViewController {
//Properties
var budgets = [Budget]()
override func viewDidLoad() {
super.viewDidLoad()
loadSampleBudgets()
}
func loadSampleBudgets() {
let budget1 = Budget(date: "8/16/2016", name: "Eyebrows", amount: "15")!
let budget2 = Budget(date: "8/28/2016", name: "Acme", amount: "59")!
let budget3 = Budget(date: "9/10/2016", name: "Wildwood", amount: "199")!
budgets += [budget1, budget2, budget3]
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return budgets.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cellIdentifier = "BudgetTableViewCell"
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as! BudgetTableViewCell
let budget = budgets[indexPath.row]
cell.dateLabel.text = budget.date
cell.nameLabel.text = budget.name
cell.amountLabel.text = budget.amount
return cell
}
#IBAction func unwindToMealList(sender: UIStoryboardSegue) {
if let sourceViewController = sender.sourceViewController as? BudgetViewController, budget = sourceViewController.budget {
//Add a new meal
let newIndexPath = NSIndexPath(forRow: budgets.count, inSection: 0)
budgets.append(budget)
tableView.insertRowsAtIndexPaths([indexPath], withRowanimation: .Bottom)
}
}
Check to see if your text fields outlets are properly linked to your storyboard. There should be a filled circle next to each IBOutlet. If you have both the code and storyboard open in XCode and hover your mouse over the circle next to each outlet, the text field should highlight on the storyboard.
If you copied and pasted the original name text field and changed it to the amount text field, it actually still might have a reference to the name outlet. You can check this by right clicking on the text field in the storyboard and see what outlets it refers to.
override func viewDidLoad() {
super.viewDidLoad()
nameTextField.delegate = self
streetTextField.delegate = self
cityTextField.delegate = self
stateTextField.delegate = self
countryTextField.delegate = self
phoneTextField.delegate = self
}
func textFieldShouldReturn(textField: UITextField) -> Bool {
if textField == nameTextField {
streetTextField.becomeFirstResponder()
}
else if textField == streetTextField {
cityTextField.becomeFirstResponder()
}
else if textField == cityTextField {
stateTextField.becomeFirstResponder()
}
else if textField == stateTextField {
countryTextField.becomeFirstResponder()
}
else if textField == countryTextField {
phoneTextField.becomeFirstResponder()
}
else if textField == phoneTextField {
nameTextField.becomeFirstResponder()
}
return true
}
This is some code I wrote to allow a user to fill in a form (pressing return to move from one field to the next), so you can definitely have multiple textFields with the same delegate.

Wait for user to dismiss Modal View before executing code (Swift 2.0)

I'm building an app that asks users to select a location if they don't allow access to their current location using a Modal that Presents Modally as soon as the user clicks 'Deny'. This modal has information displayed as a TableView, and the modal dismisses as soon as the user selects a row. I save this selection in a variable called selectedStop. I want the app to pause until the user selects a location, then as soon as the user selects a location, the app continues and the setUpMap() function executes. I've tried using an infinite while loop in setUpMap() and using a boolean to break out of it as soon as a user selects a row, but the while loop executes before the Modal even pops up.
ViewController.swift
class ViewController: UIViewController {
var selectedStop: Int!
override func viewDidLoad() {
super.viewDidLoad()
// If we don't have access to the user's current location, request for it
if (CLLocationManager.authorizationStatus() != CLAuthorizationStatus.AuthorizedWhenInUse) {
locationManager.requestWhenInUseAuthorization()
}
}
func setUpMap() {
// do stuff with var selectedStop
}
func locationManager(manager: CLLocationManager, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
switch status {
case .Denied:
// if user denies access, display modal
self.performSegueWithIdentifier("NotifyModally", sender: self)
setUpMap() // need this func to execute AFTER location is selected
break
case .AuthorizedWhenInUse:
setUpMap()
break
default:
break
}
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if (segue.identifier == "NotifyModally") {
let destViewController:ModalViewController = segue.destinationViewController as! ModalViewController
// send selectedStop var to ModalViewController
destViewController.selectedStop = selectedStop
}
}
}
ModalViewController.swift
class ModalViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var tableView: UITableView!
var busStops = ["Stop 1", "Stop 2", "Stop 3"]
var selectedStop: Int!
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return busStops.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = UITableViewCell()
cell.textLabel!.text = busStops[indexPath.row]
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
selectedStop = indexPath.row
dismissViewControllerAnimated(true, completion: nil)
}
}
Using a Int variable to pass information will not working since it's a value type which will get copied every time you pass it around. So that means when you change the selectedStop in the didSelectRowAtIndexPath method, the original selectedStop inside ViewController will still be nil or whatever it was.
And then, to answer your question. There are several ways to solve this.
You can either pass a block (instead an int) to the ModalViewController like this:
var stopSelectedHandler: (Int) -> Void = { selectedStop in
// Do something here.
// setUpMap()
}
You'll call this block inside the completion handler of dismissViewControllerAnimated.
You can use notification.
// Do this inside `ViewController`.
NSNotificationCenter.defaultCenter().addObserver(self, selector: "setupMap:", name: "UserDidSelectStop", object: nil)
// And then post the notification inside `didSelectRowAtIndexPath`
NSNotificationCenter.defaultCenter().postNotificationName("UserDidSelectStop", object: nil, userInfo: ["selectedStop": 2])
// Change your setupMap to this
func setupMap(notification: NSNotification) {
guard let selectedStop = notification.userInfo?["selectedStop"] as? Int else { return }
// Now you can use selectedStop.
}
You can also use KVO, delegate, etc. Use whatever suits you.
Put the block like this:
class ViewController: UIViewController {
var stopSelectedHandler: (Int) -> Void = { selectedStop in
// Do something here.
// setUpMap()
}
....
}

Object in array is replaced when trying to append it via prepareForSegue

When I run my code, everything runs fine without errors. However, when I try to add a cell to my table view using an add button, a segue, and a text field, I am unable to do so. What happens is that when I append the object (custom) to the array that is used to create the cells, the array is emptied and filled again. The result of this is just having one cell at the table view at one time. The code is below..
Routine.swift:
struct Routine {
var name: String?
var desc: String?
}
RoutineListViewController.swift:
class RoutineListViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var routines = [Routine]()
override func viewDidLoad() {
super.viewDidLoad()
println("There are \(routines.count) routine(s).")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return routines.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("RoutineCell", forIndexPath: indexPath) as UITableViewCell
var cellName = routines[indexPath.row].name!
cell.textLabel?.text = cellName
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
}
}
EditRoutineViewController:
class EditRoutineViewController: UIViewController {
var newRoutine = Routine(name: "", desc: "")
var routineName: String?
var routineDescription: String?
var allTextFields: UITextField?
#IBOutlet weak var nameField: UITextField!
#IBOutlet weak var descriptionField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func textFieldShouldReturn(textField: UITextField) -> Bool {
allTextFields = textField
if (textField.placeholder == "Name") {
if (textField.text != nil) {
routineName = textField.text
println("Routine name set as '\(routineName)'")
}
textField.resignFirstResponder()
return true
} else if (textField.placeholder == "Description") {
if (textField.text != nil) {
routineDescription = textField.text
println("Routine description set as '\(routineDescription!)'")
}
textField.resignFirstResponder()
return true
}
return true
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let navController = segue.destinationViewController as UINavigationController
let RoutineListController = navController.topViewController as RoutineListViewController
println("There are \(RoutineListController.routines.count) routines in the routines array.")
RoutineListController.routines.append(newRoutine)
println("There are \(RoutineListController.routines.count) routines in the routines array.")
println("A new routine called \(newRoutine.name!) has been added to the routines array.")
}
#IBAction func cancel(sender: AnyObject) {
allTextFields?.endEditing(true)
self.dismissViewControllerAnimated(true, completion: nil)
}
#IBAction func done(sender: AnyObject) {
routineName = self.nameField.text
routineDescription = self.descriptionField.text
if (self.nameField.text == "") {
// Display alert view
let nameAlert = UIAlertController(title: "Oops!", message:
"Make sure you enter a name for the routine", preferredStyle: UIAlertControllerStyle.Alert)
nameAlert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.Cancel, handler: nil))
self.presentViewController(nameAlert, animated: true, completion: nil)
} else {
println("A routine called \(self.routineName!) is about to be created.")
if (self.routineDescription != nil) {
println("It has a description of '\(self.routineDescription!)'.")
}
var localRoutine = Routine(name: routineName, desc: routineDescription?)
self.newRoutine = localRoutine
view.endEditing(true)
self.performSegueWithIdentifier("showRoutines", sender: nil)
}
}
}
I'd venture a guess that the problem lies in the code in EditRoutineViewController which saves the new routine and exits/navigates back to the RoutineViewController view. Namely, with this piece of code:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let navController = segue.destinationViewController as UINavigationController
let RoutineListController = navController.topViewController as RoutineListViewController
RoutineListController.routines.append(newRoutine)
}
you create a new instance of RoutineViewController and push it on the navigation stack. Since RoutineViewController starts with an empty Routine array, you just add the single, newly-created item. And so on, and so on (I can't see from your code how you call the edit window from the RoutineViewController, but it seems it works like this).
If this is indeed the case, then you'll need to change the application flow a little bit. So, I'm presuming that the app starts from the RoutineViewController view, which should then modally show EditRoutineViewController as soon as you tap on the 'Add' or '+' button. EditRoutineViewController should also define a protocol (let's call it EditRoutineDelegate) with two methods:
protocol EditRoutineDelegate {
func didSaveRoutine(routine: Routine)
func didCancelEnteringRoutine()
}
You'd need to modify the routine-saving routine (chuckle chuckle) to something like this:
} else {
var localRoutine = Routine(name: routineName, desc: routineDescription?)
self.newRoutine = localRoutine
view.endEditing(true)
self.delegate.didSaveRoutine(self.newRoutine)
}
So, now, when you enter a new routine in EditRoutineViewController it calls its delegate, RoutineViewController to execute the adding of the new routine and refreshing of the table view. The implementation could be something like this:
func didSaveRoutine(routine: Routine) {
routines.append(routine)
tableView.reloadData()
navigationController.dismissViewControllerAnimated(true, completion: nil)
}
And the didCancelEnteringRoutine can just dismiss the modal window.
I'd be glad to elucidate any details I failed to mention.
PS: I presume you have working knowledge on how delegation works. If not, it would be a great idea to spend some time learning about the concept, since it's a widely used pattern in Cocoa.
EDIT: Here's a small demo project which has the functionality you need. I hope it will be clearer this way.