Data not being transferred between view controllers Swift - ios

When a EventTableViewController proceeds to UserEventViewController or EventViewController the data is not transferred as the labels etc. are not updated to the transferred information. I have tried fix it but it still doesn't work. Please Help.
Any help would be great thanks.
Link to project:
https://www.dropbox.com/s/1d4d8opuxzpcuk4/TicketekApp.zip?dl=0
Code:
// EventTableViewController.swift
import UIKit
class EventTableViewController: UITableViewController {
// MARK: Properties
var events = [Event]()
var isAdmin = true
override func viewDidLoad() {
super.viewDidLoad()
// Use the edit button item provided by the table view controller.
navigationItem.leftBarButtonItem = editButtonItem()
// Load any saved events, otherwise load sample data.
if let savedEvents = loadEvents() {
events += savedEvents
} else {
// Load the sample data.
loadSampleEvents()
}
}
func loadSampleEvents() {
let photo1 = UIImage(named: "event1")!
let event1 = Event(name: "ACDC", photo: photo1, rating: 4, price: 500.0, eventDescription: "Album", album: "Album1")!
let photo2 = UIImage(named: "event2")!
let event2 = Event(name: "Cold Play", photo: photo2, rating: 5, price: 500.0, eventDescription: "Album", album: "Album1")!
let photo3 = UIImage(named: "event3")!
let event3 = Event(name: "One Direction", photo: photo3, rating: 3, price: 500.0, eventDescription: "Album", album: "Album1")!
events += [event1, event2, event3]
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return events.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
// Table view cells are reused and should be dequeued using a cell identifier.
let cellIdentifier = "EventTableViewCell"
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as! EventTableViewCell
// Fetches the appropriate event for the data source layout.
let event = events[indexPath.row]
cell.nameLabel.text = event.name
cell.photoImageView.image = event.photo
cell.ratingControl.rating = event.rating
cell.priceLabel.text = event.album
return cell
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let index = self.tableView.indexPathForSelectedRow?.row
//use the index to know which cell you selected
//check for your condition here something like
if isAdmin {
performSegueWithIdentifier("eventViewControllerSegue", sender: self)
} else {
performSegueWithIdentifier("userEventTableViewControllerSegue", sender: self)
}
}
// Override to support conditional editing of the table view.
override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
// Return false if you do not want item to be editable.
return true
}
// Override to support editing the table view.
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == .Delete {
// Delete the row from the data source
events.removeAtIndex(indexPath.row)
saveEvents()
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
} else if editingStyle == .Insert {
// Create new instance of class, add to the array, and add a new row to the table
}
}
/*
// Override to support rearranging the table view.
override func tableView(tableView: UITableView, moveRowAtIndexPath fromIndexPath: NSIndexPath, toIndexPath: NSIndexPath) {
}
*/
/*
// Override to support conditional rearranging of the table view.
override func tableView(tableView: UITableView, canMoveRowAtIndexPath indexPath: NSIndexPath) -> Bool {
// Return false if you do not want the item to be re-orderable.
return true
}
*/
// MARK: - Navigation
// preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "eventViewControllerSegue" {
let eventDetailViewController = segue.destinationViewController as! EventViewController
// Get the cell that generated this segue.
if let selectedEventCell = sender as? EventTableViewCell {
let indexPath = tableView.indexPathForCell(selectedEventCell)!
let selectedEvent = events[indexPath.row]
eventDetailViewController.event = selectedEvent
}
} else if segue.identifier == "userEventTableViewControllerSegue" {
let eventDetailViewController = segue.destinationViewController as! UserEventViewController
// Get the cell that generated this segue.
if let selectedEventCell = sender as? EventTableViewCell {
let indexPath = tableView.indexPathForCell(selectedEventCell)!
let selectedEvent = events[indexPath.row]
eventDetailViewController.event = selectedEvent
}
}
else if segue.identifier == "AddItem" {
print("Adding new event.")
}
}
#IBAction func unwindToMealList(sender: UIStoryboardSegue) {
if let sourceViewController = sender.sourceViewController as? EventViewController, event = sourceViewController.event {
if let selectedIndexPath = tableView.indexPathForSelectedRow {
// Update an existing event.
events[selectedIndexPath.row] = event
tableView.reloadRowsAtIndexPaths([selectedIndexPath], withRowAnimation: .None)
} else {
// Add a new event.
let newIndexPath = NSIndexPath(forRow: events.count, inSection: 0)
events.append(event)
tableView.insertRowsAtIndexPaths([newIndexPath], withRowAnimation: .Bottom)
}
// Save the events.
saveEvents()
}
}
// MARK: NSCoding
func saveEvents() {
let isSuccessfulSave = NSKeyedArchiver.archiveRootObject(events, toFile: Event.ArchiveURL.path!)
if !isSuccessfulSave {
print("Failed to save events...")
}
}
func loadEvents() -> [Event]? {
return NSKeyedUnarchiver.unarchiveObjectWithFile(Event.ArchiveURL.path!) as? [Event]
}
}

Shripada's answer is correct, no doubt. But, I would like to explain why your code ain't working.
You are calling performSegueWithIdentifier("eventViewControllerSegue", sender: self) from tableView:didSelectRowAtIndexPath method. Just note here that sender: self which is the ViewController and not the tableCell. Now, when this fires prepareForSegue, you are trying to access the selected cell using let selectedEventCell = sender as? EventTableViewCell . But, the sender is not the EventTableCell. Thats why you get a nil value and your if condition fails.
A simple fix would be to get the selected tableViewCell using tableView.indexPathForSelectedRow! in prepareForSegue and pass the appropriate data to destinationViewController.
let eventDetailViewController = segue.destinationViewController as! EventViewController
// Get the indexPath of selected cell
let indexPath = self.tableView.indexPathForSelectedRow!
let selectedEvent = events[indexPath.row]
eventDetailViewController.event = selectedEvent

You typically want to find the event related to the row selected. So, you will need to record which row is selected. Introduce a variable called as:
var currentlySelectedIndex = 0
to your EventTableViewController
Then change the implementation of didSelectRow delegate method in the same class to-
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
//Record the row selected
currentlySelectedIndex = indexPath.row
//check for your condition here something like
if isAdmin {
performSegueWithIdentifier("eventViewControllerSegue", sender: self)
} else {
performSegueWithIdentifier("userEventTableViewControllerSegue", sender: self)
}
}
And thirdly, you will need to utilise this selected row index to fetch right event object:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "eventViewControllerSegue" {
let eventDetailViewController = segue.destinationViewController as! EventViewController
//Get the associated event
eventDetailViewController.event = events[currentlySelectedIndex]
} else if segue.identifier == "userEventTableViewControllerSegue" {
let eventDetailViewController = segue.destinationViewController as! UserEventViewController
//Get the associated event
eventDetailViewController.event = events[currentlySelectedIndex]
}
else if segue.identifier == "AddItem" {
print("Adding new event.")
}
}
This will resolve your issues.

Related

Segue not working in Swift

I'm trying to pass a certain part of an array to another view controller based on which tableview cell you click on. So when I click on the third cell for example, it'll send the array data from the third cell to the next screen. This data is an array called "todos". When I try to receive the data in the next screen, there is no data.
Code:
func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "EditTodo" {
print("it is")
var vc = segue.destination as! ViewController
// var indexPath = tableView.indexPathForCell(sender as UITableViewCell)
var indexPath = tableView.indexPathForSelectedRow
if let index = indexPath {
vc.todo = todos[index.row]
}
}
}
I'm not sure if it's being called at all, or what. The identifier is correct, and I'm not sure what else to do. (When I run it, the print function is not called, but I'm not even sure if it's supposed to be).
Here is the whole page of code from the 'sending data' page with the table:
import UIKit
var todos: [TodoModel] = []
var filteredTodos: [TodoModel] = []
class HomeViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, UISearchDisplayDelegate {
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func viewDidAppear(_ animated: Bool) {
print("i think it worked...")
let defaults = UserDefaults.standard
if todos.count > 0 {
// Save what we have
let data = NSKeyedArchiver.archivedData(withRootObject: todos)
defaults.set(data, forKey: "TDDATA")
defaults.synchronize()
print("saved \(todos.count)")
} else if let storedTodoData = defaults.data(forKey: "TDDATA"),
let storedTodos = NSKeyedUnarchiver.unarchiveObject(with: storedTodoData) as? [TodoModel] {
// There was stored data! Use it!
todos = storedTodos
print("Used \(todos.count) stored todos")
tableView.reloadData()
self.tableView.tableFooterView = UIView()
}
//print([todos.first])
print("Here?: \(todos.first?.title)")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override var prefersStatusBarHidden: Bool {
return true
}
/*
// 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.
}
*/
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if tableView == searchDisplayController?.searchResultsTableView {
return filteredTodos.count
}
else {
return todos.count
}
}
// Display the cell
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// Must use 'self' here because searchResultsTableView needs to reuse the same cell in self.tableView
let cell = self.tableView.dequeueReusableCell(withIdentifier: "todoCell")! as UITableViewCell
var todo : TodoModel
if tableView == searchDisplayController?.searchResultsTableView {
todo = filteredTodos[indexPath.row] as TodoModel
}
else {
todo = todos[indexPath.row] as TodoModel
}
//var image = cell.viewWithTag(101) as! UIImageView
var title = cell.viewWithTag(102) as! UILabel
var date = cell.viewWithTag(103) as! UILabel
//image.image = todo.image
// image = UIImageView(image: newImage)
// if image.image == nil{
// print("nilish")
// image = UIImageView(image: UIImage(named: "EmptyProfile.png"))
// }
// image.image = todo.image
// if image.image == nil{
// print("pic is nil")
// image.image = UIImage(named: "CopyEmptyProfilePic.jpg")
// }
title.text = todo.title
date.text = "\(NSDate())"
let locale = NSLocale.current
let dateFormat = DateFormatter.dateFormat(fromTemplate: "yyyy-MM-dd", options:0, locale:locale)
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = dateFormat
return cell
}
// MARK - UITableViewDelegate
// Delete the cell
func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == UITableViewCellEditingStyle.delete {
todos.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath as IndexPath], with: UITableViewRowAnimation.automatic)
let defaults = UserDefaults.standard
if todos.count >= 0 {
// Save what we have
let data = NSKeyedArchiver.archivedData(withRootObject: todos)
defaults.set(data, forKey: "TDDATA")
defaults.synchronize()
print("saved \(todos.count)")
} else if let storedTodoData = defaults.data(forKey: "TDDATA"),
let storedTodos = NSKeyedUnarchiver.unarchiveObject(with: storedTodoData) as? [TodoModel] {
// There was stored data! Use it!
todos = storedTodos
print("Used \(todos.count) stored todos")
}
tableView.reloadData()
}
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return 80
}
// Edit mode
override func setEditing(_ editing: Bool, animated: Bool) {
super.setEditing(editing, animated: animated)
tableView.setEditing(editing, animated: true)
}
// Move the cell
func tableView(tableView: UITableView, canMoveRowAtIndexPath indexPath: NSIndexPath) -> Bool {
return self.isEditing
}
// func tableView(tableView: UITableView, moveRowAtIndexPath sourceIndexPath: NSIndexPath, toIndexPath destinationIndexPath: NSIndexPath) {
// let todo = todos.removeAtIndex(sourceIndexPath.row)
//todos.insert(todo, atIndex: destinationIndexPath.row)
// }
// MARK - UISearchDisplayDelegate
// Search the Cell
func searchDisplayController(controller: UISearchDisplayController, shouldReloadTableForSearchString searchString: String?) -> Bool {
//filteredTodos = todos.filter({( todo: TodoModel) -> Bool in
// let stringMatch = todo.title.rangeOfString(searchString)
// return stringMatch != nil
//})
// Same as below
filteredTodos = todos.filter(){$0.title.range(of: searchString!)
!= nil}
return true
}
// MARK - Storyboard stuff
// Unwind
#IBAction func close(segue: UIStoryboardSegue) {
print("closed!")
tableView.reloadData()
}
// Segue
func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "EditTodo" {
print("it is")
var vc = segue.destination as! ViewController
// var indexPath = tableView.indexPathForCell(sender as UITableViewCell)
var indexPath = tableView.indexPathForSelectedRow
if let index = indexPath {
vc.todo = todos[index.row]
}
}
}
}
How I am receiving the data in viewDidLoad of receiving screen:
nameOfDocTF.delegate = self
nameOfDocTF.text = todo?.title
outputTextView.attributedText = todo?.desc
print(todo?.title)
//prints "nil"
As I suspected you´re missing the didSelectRowAt function which will be called when you click on a cell and from there you need to call your prepareForSegue. Implement the following function and try:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.performSegue(withIdentifier: "EditTodo", sender: indexPath.row)
}
And then replace your prepareForSegue function with the following:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "EditTodo" {
let vc = segue.destination as! ViewController
if let index = sender as? Int {
vc.todo = todos[index]
}
}
}

Update or reload UITableView after completion of delete action on detail view

I have a uitableview that collects data from mysql through json. Then it has a detail view that has two action edit and delete. Edit works fine. Delete action deletes mysql data but problem is it does not update data from uitableview.
Here is screen shot and code
//Table View Controller
import UIKit
class TableViewController: UITableViewController {
var storeList = [Store]()
//var storeList:Store?
override func viewDidLoad() {
super.viewDidLoad()
/*
if let s = storeList
{
txtName.text = s.storeName
}
*/
// Uncomment the following line to preserve selection between presentations
//self.clearsSelectionOnViewWillAppear = true
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem()
self.loadRecords()
self.tableView.reloadData()
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
self.tableView.reloadData() // to reload selected cell
//tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.Automatic)
}
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 {
// #warning Incomplete implementation, return the number of rows
return storeList.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
//let cell = tableView.dequeueReusableCellWithIdentifier("reuseIdentifier", forIndexPath: indexPath)
// Configure the cell...
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! StoreTVC
let s = storeList[indexPath.row] as Store
cell.lblName.text = s.storeName
//cell.lblID.text = s.storeId
return cell
}
// for swipe delete
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == UITableViewCellEditingStyle.Delete {
storeList.removeAtIndex(indexPath.row)
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.Automatic)
}
}
/*
// Override to support conditional editing of the table view.
override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
// Return false if you do not want the specified item to be editable.
return true
}
*/
/*
// Override to support editing the table view.
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == .Delete {
// Delete the row from the data source
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
} else if editingStyle == .Insert {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
}
}
*/
/*
// Override to support rearranging the table view.
override func tableView(tableView: UITableView, moveRowAtIndexPath fromIndexPath: NSIndexPath, toIndexPath: NSIndexPath) {
}
*/
/*
// Override to support conditional rearranging of the table view.
override func tableView(tableView: UITableView, canMoveRowAtIndexPath indexPath: NSIndexPath) -> Bool {
// Return false if you do not want the item to be re-orderable.
return true
}
*/
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
/*
if segue.identifier == "details"
{
//if let indexPath = tableView.indexPathForCell(sender as! UITableViewCell)
if let indexPath = tableView.indexPathForSelectedRow
{
let s = storeList[indexPath.row] as Store
let dvc = segue.destinationViewController as! ViewDetails
dvc.store = s
}
}
*/
if let indexPath = tableView.indexPathForCell(sender as! UITableViewCell)
{
let s = storeList[indexPath.row] as Store
let dvc = segue.destinationViewController as! ViewDetails
dvc.store = s
}
}
func loadRecords()
{
//The address of the web service
let urlString = "http://localhost/crud/read_for_table_view.php"
// 1 - Create the session by getting the configuration and then crrating the session
let config = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: config, delegate: nil, delegateQueue: nil)
//2 - Create the URL Object
if let url = NSURL(string: urlString)
{
//3 - Create the request object
let request = NSURLRequest(URL: url)
//4 - execute the request
let taskData = session.dataTaskWithRequest(request, completionHandler: {
(data: NSData?, response:NSURLResponse?, error: NSError?) -> Void in
//5 - Do something with the Data back
if(data != nil)
{
//we got some data back
print("\(data)")
/*
var parseError:NSError?
let parsedStores = NSJSONSerialization.JSONObjectWithData(data, options: nil, error: &parseError) as! NSDictionary
*/
do {
if let parsedStores = try NSJSONSerialization.JSONObjectWithData(data!, options: []) as? NSDictionary {
print("Json Data \n \(parsedStores)")
if let stores:AnyObject = parsedStores["result"]
{
self.parseJSON(stores)
}
}
} catch let error as NSError {
print(error.localizedDescription)
}
}else
{
//we got an error
print("Error getting stores :\(error!.localizedDescription)")
}
})
taskData.resume()
}
}
func parseJSON(jsonData:AnyObject)
{
if let storeData = jsonData as? [[NSObject:AnyObject]]
{
var store:Store
//we loop through all the recors and everytime we create
// an object of kind store and then add to the store list
for s in storeData
{
store = Store()
// this part is getting the values
if let sId:AnyObject = s["id"]
{
if let storeID = sId as? String
{
print("Store id = \(storeID)")
store.storeId = storeID
}
}
if let sn:AnyObject = s["name"]
{
if let storeName = sn as? String
{
store.storeName = storeName
}
}
storeList += [store]
}
NSOperationQueue.mainQueue().addOperationWithBlock()
{
self.tableView.reloadData()
}
}
}
}
//Detail View
import UIKit
class TableViewController: UITableViewController {
var storeList = [Store]()
//var storeList:Store?
override func viewDidLoad() {
super.viewDidLoad()
/*
if let s = storeList
{
txtName.text = s.storeName
}
*/
// Uncomment the following line to preserve selection between presentations
//self.clearsSelectionOnViewWillAppear = true
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem()
self.loadRecords()
self.tableView.reloadData()
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
self.tableView.reloadData() // to reload selected cell
//tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.Automatic)
}
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 {
// #warning Incomplete implementation, return the number of rows
return storeList.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
//let cell = tableView.dequeueReusableCellWithIdentifier("reuseIdentifier", forIndexPath: indexPath)
// Configure the cell...
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! StoreTVC
let s = storeList[indexPath.row] as Store
cell.lblName.text = s.storeName
//cell.lblID.text = s.storeId
return cell
}
// for swipe delete
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == UITableViewCellEditingStyle.Delete {
storeList.removeAtIndex(indexPath.row)
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.Automatic)
}
}
/*
// Override to support conditional editing of the table view.
override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
// Return false if you do not want the specified item to be editable.
return true
}
*/
/*
// Override to support editing the table view.
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == .Delete {
// Delete the row from the data source
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
} else if editingStyle == .Insert {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
}
}
*/
/*
// Override to support rearranging the table view.
override func tableView(tableView: UITableView, moveRowAtIndexPath fromIndexPath: NSIndexPath, toIndexPath: NSIndexPath) {
}
*/
/*
// Override to support conditional rearranging of the table view.
override func tableView(tableView: UITableView, canMoveRowAtIndexPath indexPath: NSIndexPath) -> Bool {
// Return false if you do not want the item to be re-orderable.
return true
}
*/
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
/*
if segue.identifier == "details"
{
//if let indexPath = tableView.indexPathForCell(sender as! UITableViewCell)
if let indexPath = tableView.indexPathForSelectedRow
{
let s = storeList[indexPath.row] as Store
let dvc = segue.destinationViewController as! ViewDetails
dvc.store = s
}
}
*/
if let indexPath = tableView.indexPathForCell(sender as! UITableViewCell)
{
let s = storeList[indexPath.row] as Store
let dvc = segue.destinationViewController as! ViewDetails
dvc.store = s
}
}
func loadRecords()
{
//The address of the web service
let urlString = "http://localhost/crud/read_for_table_view.php"
// 1 - Create the session by getting the configuration and then crrating the session
let config = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: config, delegate: nil, delegateQueue: nil)
//2 - Create the URL Object
if let url = NSURL(string: urlString)
{
//3 - Create the request object
let request = NSURLRequest(URL: url)
//4 - execute the request
let taskData = session.dataTaskWithRequest(request, completionHandler: {
(data: NSData?, response:NSURLResponse?, error: NSError?) -> Void in
//5 - Do something with the Data back
if(data != nil)
{
//we got some data back
print("\(data)")
/*
var parseError:NSError?
let parsedStores = NSJSONSerialization.JSONObjectWithData(data, options: nil, error: &parseError) as! NSDictionary
*/
do {
if let parsedStores = try NSJSONSerialization.JSONObjectWithData(data!, options: []) as? NSDictionary {
print("Json Data \n \(parsedStores)")
if let stores:AnyObject = parsedStores["result"]
{
self.parseJSON(stores)
}
}
} catch let error as NSError {
print(error.localizedDescription)
}
}else
{
//we got an error
print("Error getting stores :\(error!.localizedDescription)")
}
})
taskData.resume()
}
}
func parseJSON(jsonData:AnyObject)
{
if let storeData = jsonData as? [[NSObject:AnyObject]]
{
var store:Store
//we loop through all the recors and everytime we create
// an object of kind store and then add to the store list
for s in storeData
{
store = Store()
// this part is getting the values
if let sId:AnyObject = s["id"]
{
if let storeID = sId as? String
{
print("Store id = \(storeID)")
store.storeId = storeID
}
}
if let sn:AnyObject = s["name"]
{
if let storeName = sn as? String
{
store.storeName = storeName
}
}
storeList += [store]
}
NSOperationQueue.mainQueue().addOperationWithBlock()
{
self.tableView.reloadData()
}
}
}
}
Can you please help ?
So I got answer.
here is the update
import UIKit
class TableViewController: UITableViewController {
var storeList = [Store]()
//var storeList:Store?
override func viewDidLoad() {
super.viewDidLoad()
/*
if let s = storeList {
txtName.text = s.storeName
}
*/
// Uncomment the following line to preserve selection between presentations
//self.clearsSelectionOnViewWillAppear = true
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem()
self.loadRecords()
self.tableView.reloadData()
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
self.loadRecords() // to reload selected cell
//tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.Automatic)
}
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 {
// #warning Incomplete implementation, return the number of rows
return storeList.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
//let cell = tableView.dequeueReusableCellWithIdentifier("reuseIdentifier", forIndexPath: indexPath)
// Configure the cell...
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! StoreTVC
let s = storeList[indexPath.row] as Store
cell.lblName.text = s.storeName
//cell.lblID.text = s.storeId
return cell
}
// for swipe delete
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == UITableViewCellEditingStyle.Delete {
storeList.removeAtIndex(indexPath.row)
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.Automatic)
}
}
/*
// Override to support conditional editing of the table view.
override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
// Return false if you do not want the specified item to be editable.
return true
}
*/
/*
// Override to support editing the table view.
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == .Delete {
// Delete the row from the data source
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
} else if editingStyle == .Insert {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
}
}
*/
/*
// Override to support rearranging the table view.
override func tableView(tableView: UITableView, moveRowAtIndexPath fromIndexPath: NSIndexPath, toIndexPath: NSIndexPath) {
}
*/
/*
// Override to support conditional rearranging of the table view.
override func tableView(tableView: UITableView, canMoveRowAtIndexPath indexPath: NSIndexPath) -> Bool {
// Return false if you do not want the item to be re-orderable.
return true
}
*/
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
/*
if segue.identifier == "details" {
//if let indexPath = tableView.indexPathForCell(sender as! UITableViewCell)
if let indexPath = tableView.indexPathForSelectedRow {
let s = storeList[indexPath.row] as Store
let dvc = segue.destinationViewController as! ViewDetails
dvc.store = s
}
}
*/
if let indexPath = tableView.indexPathForCell(sender as! UITableViewCell) {
let s = storeList[indexPath.row] as Store
let dvc = segue.destinationViewController as! ViewDetails
dvc.store = s
}
}
func loadRecords() {
//The address of the web service
let urlString = "http://localhost/crud/read_for_table_view.php"
// 1 - Create the session by getting the configuration and then crrating the session
let config = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: config, delegate: nil, delegateQueue: nil)
//2 - Create the URL Object
if let url = NSURL(string: urlString) {
//3 - Create the request object
let request = NSURLRequest(URL: url)
//4 - execute the request
let taskData = session.dataTaskWithRequest(request, completionHandler: {
(data: NSData?, response:NSURLResponse?, error: NSError?) -> Void in
//5 - Do something with the Data back
if (data != nil) {
//we got some data back
print("\(data)")
/*
var parseError:NSError?
let parsedStores = NSJSONSerialization.JSONObjectWithData(data, options: nil, error: &parseError) as! NSDictionary
*/
do {
if let parsedStores = try NSJSONSerialization.JSONObjectWithData(data!, options: []) as? NSDictionary {
print("Json Data \n \(parsedStores)")
if let stores:AnyObject = parsedStores["result"] {
self.parseJSON(stores)
}
}
} catch let error as NSError {
print(error.localizedDescription)
}
} else {
//we got an error
print("Error getting stores :\(error!.localizedDescription)")
}
})
taskData.resume()
}
}
func parseJSON(jsonData:AnyObject) {
storeList.removeAll()
if let storeData = jsonData as? [[NSObject:AnyObject]] {
var store:Store
//we loop through all the recors and everytime we create
// an object of kind store and then add to the store list
for s in storeData {
store = Store()
// this part is getting the values
if let sId:AnyObject = s["id"] {
if let storeID = sId as? String {
print("Store id = \(storeID)")
store.storeId = storeID
}
}
if let sn:AnyObject = s["name"] {
if let storeName = sn as? String {
store.storeName = storeName
}
}
storeList += [store]
}
NSOperationQueue.mainQueue().addOperationWithBlock() {
self.tableView.reloadData()
}
}
}
}
Here is the better solution for you rather than reload tableview in viewWillAppear. Approach using delegate.
On your Detail View Controller add.
weak var delegate : TableViewController!
Modify line of code on Table View Controller
if segue.identifier == "details"{
if let indexPath = tableView.indexPathForSelectedRow{
let s = storeList[indexPath.row] as Store
let dvc = segue.destinationViewController as! ViewDetails
dvc.store = s
dvc.delegate = self
}
}
When action complete on your Detail View or before dismiss details view. Execute below line in your Detail View.
self.delegate.tableView.reloadData()
Here I hope it's better way and best practice rather than your approach. Try it. :)
Try Below Code:
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == UITableViewCellEditingStyle.Delete {
storeList.removeAtIndex(indexPath.row)
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.Automatic)
self.loadRecords()
}
}
Hope this will help you
I know this is an old question, but my 2 cents are, if you want to reload your tableview, as long as when you're making any updates your storeList array in any way, you can just make use of didSet when declaring your storeList like this:
var storeList: [Store] = []{
didSet{
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
With the above, you are saying, any time the storeList array has content removed, added or updated in any way, then on the main UI, reload the tableView, this way you don't need to manually make changes to your tableView, but changes to the storeList will trigger changes to the tableView

How to perform a different segue when in Edit mode (in UITableView)?

I am creating a simple iPhone app using UITableView. I am using the default MasterDetail application template. Right now (in Edit mode) when I press any of the table cells nothing happens. However, when I am in normal mode the detail segue is initiated. How to override the Edit mode so that I initiate a custom segue to go to a different UIViewController.
P.S.: I still want to preserve the inherit delete functionality.
This is my code in my MasterViewController:
class MasterViewController: UITableViewController {
let kFileName: String = "/resolutionData.plist"
var resolutions = [Dictionary<String,String>]()
var achievedResolutions = [Dictionary<String,String>]()
// TO DO create a class to get this array
let iconArray = ["Fish","Fly","Heart","HelpingHand","Melon","Star","Tentacles","Volunteering"]
override func awakeFromNib() {
super.awakeFromNib()
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.navigationItem.leftBarButtonItem = self.editButtonItem()
let addButton = UIBarButtonItem(barButtonSystemItem: .Add, target: self, action: "insertNewObject:")
self.navigationItem.rightBarButtonItem = addButton
//extract the path
NSLog(dataFilePath())
/**
* Check where is the sandbox of the application
* and if there is read from the data file and story it to "objects" array
*/
if NSFileManager.defaultManager().fileExistsAtPath(dataFilePath()){
var temp = NSArray(contentsOfFile: dataFilePath()) as! [Dictionary<String,String>]
for res in temp{
if res["isAchieved"] == "Y"{
achievedResolutions.append(res)
}else{
resolutions.append(res)
}
}
//... if there is not - create it
} else {
let data = [["name":"Resolution name test","startingDate":"24-11-15","achievingDate":"01-01-2016","icon":iconArray[0],"isAchieved":"N"] as NSDictionary] as NSArray
//if the file does not exist...
if !NSFileManager.defaultManager().fileExistsAtPath(dataFilePath()){
//... create it
NSFileManager.defaultManager().createFileAtPath(dataFilePath(), contents: nil, attributes: nil)
}
//write to it
data.writeToFile(dataFilePath(), atomically: true)
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func insertNewObject(sender: AnyObject) {
performSegueWithIdentifier("editDetails", sender: sender)
}
func saveDateToFile(){
let data = resolutions as NSArray
data.writeToFile(dataFilePath(), atomically: true)
}
func notifyTableViewForNewInsertion(){
let indexPath = NSIndexPath(forRow: 0, inSection: 0)
self.tableView.insertRowsAtIndexPaths([indexPath], withRowAnimation: .Automatic)
}
// MARK: - Segues
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showDetail" {
if let indexPath = self.tableView.indexPathForSelectedRow() {
let object = resolutions[indexPath.row] as Dictionary
(segue.destinationViewController as! DetailViewController).detailItem = object
}
}
}
// MARK: - Table View
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 2
}
override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
if section == 0 {
return "Active"
}else{
return "Achieved"
}
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == 0 {
return resolutions.count
}else{
// TODO replace that with an actual array count
return achievedResolutions.count
}
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! UITableViewCell
if indexPath.section == 0{
let object: AnyObject? = resolutions[indexPath.row] ["name"]
cell.textLabel!.text = object as? String
cell.detailTextLabel!.text = resolutions[indexPath.row]["achievingDate"]
cell.imageView!.image = UIImage(named: resolutions[indexPath.row]["icon"]!)
} else {
let object: AnyObject? = achievedResolutions[indexPath.row] ["name"]
cell.textLabel!.text = object as? String
cell.detailTextLabel!.text = resolutions[indexPath.row]["achievingDate"]
cell.imageView!.image = UIImage(named: resolutions[indexPath.row]["icon"]!)
}
return cell
}
override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
// Return false if you do not want the specified item to be editable.
return true
}
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == .Delete {
resolutions.removeAtIndex(indexPath.row)
saveDateToFile()
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
} else if editingStyle == .Insert {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view.
}
}
func dataFilePath() -> String{
let paths = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true)
//get the first path and convert it to str
let docDicrectyory: String = paths[0] as! String
return "\(docDicrectyory)\(kFileName)"
}
}
Instead of performing your segue directly from the storyboard, add a UITableViewDelegate method:
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if tableView.editing {
performSegueWithIdentifier("id_Segue_Editing_VC", sender: tableView.cellForRowAtIndexPath(indexPath))
} else {
performSegueWithIdentifier("id_Segue_Standard_VC", sender: tableView.cellForRowAtIndexPath(indexPath))
}
}
Set the sender to the selected cell -- this matches the default UIKit behavior.
Then in your prepareForSegue method you can add custom value to your view controllers according to the segue identifier.
Override willBeginEditingRowAtIndexPath: function. this willbe call before start editing. there you can initialize a global variable.
#property(nonatomic, String) BOOL editing
and in the
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if (editing) {
if([[segue identifier] isEqualToString:#"identifierOne"]){
}else if([[segue identifier] isEqualToString:#"identifierTwo"]){
}
}
}
There is a table view property for that:
self.tableView.allowsSelectionDuringEditing = YES;

Proceed to different view controllers based on boolean value

When a table cell is selected, I am trying to proceed to a view controller if the value of a boolean is true, else (if it is false), proceed to a different view controller. Any help would be great!
(The boolean is isAdmin and if true proceed to eventViewController, else proceed to UserEventTableViewController)
link to project:
https://www.dropbox.com/s/1d4d8opuxzpcuk4/TicketekApp.zip?dl=0
Code:
import UIKit
class EventTableViewController: UITableViewController {
// MARK: Properties
var events = [Event]()
var isAdmin = false
override func viewDidLoad() {
super.viewDidLoad()
// Use the edit button item provided by the table view controller.
navigationItem.leftBarButtonItem = editButtonItem()
// Load any saved events, otherwise load sample data.
if let savedEvents = loadEvents() {
events += savedEvents
} else {
// Load the sample data.
loadSampleEvents()
}
}
func loadSampleEvents() {
let photo1 = UIImage(named: "event1")!
let event1 = Event(name: "ACDC", photo: photo1, rating: 4, price: 500.0, eventDescription: "Album", album: "Album1")!
let photo2 = UIImage(named: "event2")!
let event2 = Event(name: "Cold Play", photo: photo2, rating: 5, price: 500.0, eventDescription: "Album", album: "Album1")!
let photo3 = UIImage(named: "event3")!
let event3 = Event(name: "One Direction", photo: photo3, rating: 3, price: 500.0, eventDescription: "Album", album: "Album1")!
events += [event1, event2, event3]
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return events.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
// Table view cells are reused and should be dequeued using a cell identifier.
let cellIdentifier = "EventTableViewCell"
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as! EventTableViewCell
// Fetches the appropriate event for the data source layout.
let event = events[indexPath.row]
cell.nameLabel.text = event.name
cell.photoImageView.image = event.photo
cell.ratingControl.rating = event.rating
cell.priceLabel.text = event.album
return cell
}
// Override to support conditional editing of the table view.
override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
// Return false if you do not want item to be editable.
return true
}
// Override to support editing the table view.
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == .Delete {
// Delete the row from the data source
events.removeAtIndex(indexPath.row)
saveEvents()
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
} else if editingStyle == .Insert {
// Create new instance of class, add to the array, and add a new row to the table
}
}
/*
// Override to support rearranging the table view.
override func tableView(tableView: UITableView, moveRowAtIndexPath fromIndexPath: NSIndexPath, toIndexPath: NSIndexPath) {
}
*/
/*
// Override to support conditional rearranging of the table view.
override func tableView(tableView: UITableView, canMoveRowAtIndexPath indexPath: NSIndexPath) -> Bool {
// Return false if you do not want the item to be re-orderable.
return true
}
*/
// MARK: - Navigation
// preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "ShowDetail" {
let eventDetailViewController = segue.destinationViewController as! EventViewController
// Get the cell that generated this segue.
if let selectedEventCell = sender as? EventTableViewCell {
let indexPath = tableView.indexPathForCell(selectedEventCell)!
let selectedEvent = events[indexPath.row]
eventDetailViewController.event = selectedEvent
print(selectedEventCell)
}
}
else if segue.identifier == "AddItem" {
print("Adding new event.")
}
}
#IBAction func unwindToMealList(sender: UIStoryboardSegue) {
if let sourceViewController = sender.sourceViewController as? EventViewController, event = sourceViewController.event {
if let selectedIndexPath = tableView.indexPathForSelectedRow {
// Update an existing event.
events[selectedIndexPath.row] = event
tableView.reloadRowsAtIndexPaths([selectedIndexPath], withRowAnimation: .None)
} else {
// Add a new event.
let newIndexPath = NSIndexPath(forRow: events.count, inSection: 0)
events.append(event)
tableView.insertRowsAtIndexPaths([newIndexPath], withRowAnimation: .Bottom)
}
// Save the events.
saveEvents()
}
}
// MARK: NSCoding
func saveEvents() {
let isSuccessfulSave = NSKeyedArchiver.archiveRootObject(events, toFile: Event.ArchiveURL.path!)
if !isSuccessfulSave {
print("Failed to save events...")
}
}
func loadEvents() -> [Event]? {
return NSKeyedUnarchiver.unarchiveObjectWithFile(Event.ArchiveURL.path!) as? [Event]
}
}
You have to implement the tableView delegate method didSelectRowAtIndexPath.
example as follows:
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let index = self.tableView.indexPathForSelectedRow?.row
//use the index to know which cell you selected
//check for your condition here something like
if isAdmin {
performSegueWithIdentifier("eventViewControllerSegue", sender: self)
} else {
performSegueWithIdentifier("userEventTableViewControllerSegue", sender: self)
}
}
note the following:
In your story board, draw segues to the different view controllers and assign the segue ID respectively.
In your code you are not changing the value of isAdmin boolean. Check that too.
Regards.
Image on how to segue from ViewController for your reference
I believe you can accomplish what you are seeking by using tableView:didSelectRowAtIndexPath: from the UITableViewDelegate protocol.
Add this to your view controller
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
and handle your logic within that function.
When you are ready to go to the next view controller, use
performSegueWithIdentifier(identifier: String, sender: AnyObject?)
Here is an example:
if isAdmin {
performSegueWithIdentifier(identifier: "segueToAdminView", sender: self)
} else {
performSegueWithIdentifier(identifier: "segueToUserEventView", sender: self)
}

Object isn't updating in tableview with new Realm data

Whenever I click on a row in my tableview and change the text in the fields to something else, it always crashes and doesn't actually update the data. I'm saving all my data in a "Realm" database, which im fairly new at using.
I'm doing the updating in the "unwindToNoteList" function. Can someone help me know why it's adding a new row and crashing instead of updating the current row?
import UIKit
import Realm
class NoteTableViewController: UITableViewController {
// MARK: Properties
var notes = Note.allObjects()
override func viewDidLoad() {
super.viewDidLoad()
// Get Realm Database location
println(RLMRealm.defaultRealm().path)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// Return the number of sections.
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// Return the number of rows in the section.
return Int(notes.count)
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
// Table view cells are reused and should be dequeued using a cell identifier.
let cellIdentifier = "NoteTableViewCell"
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as! NoteTableViewCell
let object = notes[UInt(indexPath.row)] as! Note
// Configure the cell...
cell.titleLabel.text = object.title
cell.bodyLabel.text = object.body
return cell
}
// Override to support conditional editing of the table view.
override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
// Return NO if you do not want the specified item to be editable.
return true
}
// Override to support editing the table view.
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == .Delete {
// Delete from database
let realm = RLMRealm.defaultRealm()
realm.beginWriteTransaction()
realm.deleteObject(notes[UInt(indexPath.row)] as! RLMObject)
realm.commitWriteTransaction()
// Delete row from table view
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
} else if editingStyle == .Insert {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
}
}
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "ShowDetail" { // Clicked on a cell
let noteDetailViewController = segue.destinationViewController as! NoteViewController
// Get the cell that generated this segue.
if let selectedNoteCell = sender as? NoteTableViewCell {
let indexPath = tableView.indexPathForCell(selectedNoteCell)!
let selectedNote = notes[UInt(indexPath.row)] as! Note
noteDetailViewController.note = selectedNote
}
}
else if segue.identifier == "AddItem" { // Clicked add button
println("Adding new note")
}
}
#IBAction func unwindToNoteList(sender: UIStoryboardSegue) {
if let selectedIndexPath = tableView.indexPathForSelectedRow() { // User clicked on a row
// Update an existing note.
let realm = RLMRealm.defaultRealm()
realm.beginWriteTransaction()
Note.createOrUpdateInRealm(realm, withValue: ["title": note.title, "body": note.body, "id": note.id])
realm.commitWriteTransaction()
println("Yes")
tableView.reloadRowsAtIndexPaths([selectedIndexPath], withRowAnimation: .None)
}
else {
// Add a new note.
let newIndexPath = NSIndexPath(forRow: Int(notes.count), inSection: 0)
// Persist in database
let realm = RLMRealm.defaultRealm()
realm.beginWriteTransaction()
Note.createInRealm(realm, withValue: ["title": note.title, "body": note.body, "id": uuid])
realm.commitWriteTransaction()
println("no")
tableView.insertRowsAtIndexPaths([newIndexPath], withRowAnimation: .Bottom)
}
}
}
}
Finally solved this, by not updating the object while in the tableview but while in the view controller that holds the text fields
Here's my NoteViewController
import UIKit
import Realm
class NoteViewController: UIViewController, UITextFieldDelegate, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
// MARK: Properties
#IBOutlet var saveButton: UIBarButtonItem!
#IBOutlet var titleTextField: UITextField!
#IBOutlet var bodyTextField: UITextField!
/*
This value is either passed by `NoteTableViewController` in `prepareForSegue(_:sender:)`
or constructed as part of adding a new note.
*/
var note = Note?()
// MARK: Navigation
#IBAction func cancel(sender: UIBarButtonItem) {
// Depending on style of presentation (modal or push presentation), this view controller needs to be dismissed in two different ways.
let isPresentingInAddNoteMode = presentingViewController is UINavigationController
if isPresentingInAddNoteMode {
dismissViewControllerAnimated(true, completion: nil)
} else {
navigationController!.popViewControllerAnimated(true)
}
}
// This method lets you configure a view controller before it's presented.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if saveButton === sender {
let title = titleTextField.text ?? ""
let body = bodyTextField.text ?? ""
/* The operator ?? unwraps the optional string in titleTextField.text since it may or may not have text in the field, and returns the value if it's a valid string. If nil though, it returns and empty string ("") instead
*/
// Update the database
let realm = RLMRealm.defaultRealm()
realm.beginWriteTransaction()
note?.title = titleTextField.text
note?.body = bodyTextField.text
realm.commitWriteTransaction()
// Set the note to be passed to NoteTableViewController after the unwind segue.
// AKA pass "note" to NoteTableViewController with whatever the text fields have in them
note = Note(title: title, body: body)
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
// Handle the text field’s user input through delegate callbacks.
titleTextField.delegate = self
// Set up views if editing an existing Note.
if let note = note {
navigationItem.title = note.title
titleTextField.text = note.title
bodyTextField.text = note.body
}
// Enable the Save button only if the text field has a valid Note name.
checkValidNoteName()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: UITextFieldDelegate
func textFieldDidBeginEditing(textField: UITextField) {
// Disable the Save button while editing.
saveButton.enabled = false
}
func checkValidNoteName() {
// Disable the Save button if the text field is empty.
let text = titleTextField.text ?? ""
saveButton.enabled = !text.isEmpty
}
func textFieldDidEndEditing(textField: UITextField) {
checkValidNoteName()
navigationItem.title = textField.text
}
}
And here's the original, NoteTableViewController. Updated a bit as well.
import UIKit
import Realm
class NoteTableViewController: UITableViewController {
// MARK: Properties
var notes = Note.allObjects()
override func viewDidLoad() {
super.viewDidLoad()
// Get Realm Database location
println(RLMRealm.defaultRealm().path)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// Return the number of sections.
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// Return the number of rows in the section.
return Int(notes.count)
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
// Table view cells are reused and should be dequeued using a cell identifier.
let cellIdentifier = "NoteTableViewCell"
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as! NoteTableViewCell
let object = notes[UInt(indexPath.row)] as! Note
// Configure the cell...
cell.titleLabel.text = object.title
cell.bodyLabel.text = object.body
return cell
}
// Override to support conditional editing of the table view.
override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
// Return NO if you do not want the specified item to be editable.
return true
}
// Override to support editing the table view.
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == .Delete {
// Delete from database
let realm = RLMRealm.defaultRealm()
realm.beginWriteTransaction()
realm.deleteObject(notes[UInt(indexPath.row)] as! RLMObject)
realm.commitWriteTransaction()
// Delete row from table view
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
} else if editingStyle == .Insert {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
}
}
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "ShowDetail" { // Clicked on a cell
let noteDetailViewController = segue.destinationViewController as! NoteViewController
// Get the cell that generated this segue.
if let selectedNoteCell = sender as? NoteTableViewCell {
let indexPath = tableView.indexPathForCell(selectedNoteCell)!
let selectedNote = notes[UInt(indexPath.row)] as! Note
noteDetailViewController.note = selectedNote
}
}
else if segue.identifier == "AddItem" { // Clicked add button
println("Adding new note")
}
}
#IBAction func unwindToNoteList(sender: UIStoryboardSegue) {
if let sourceViewController = sender.sourceViewController as? NoteViewController, note = sourceViewController.note {
let uuid = NSUUID().UUIDString // Needed for primary key. see below
var unwindedNote = Note()
if let selectedIndexPath = tableView.indexPathForSelectedRow() { // User clicked on a row
// Updating of the note is done in NoteViewController
tableView.reloadRowsAtIndexPaths([selectedIndexPath], withRowAnimation: .None)
}
else {
// Add a new note.
let newIndexPath = NSIndexPath(forRow: Int(notes.count), inSection: 0)
// Persist in database
let realm = RLMRealm.defaultRealm()
realm.beginWriteTransaction()
unwindedNote.title = note.title
unwindedNote.body = note.body
unwindedNote.id = uuid // This is done for the primary key that Realm needs, unique for each object created.
realm.addObjects([unwindedNote])
realm.commitWriteTransaction()
tableView.insertRowsAtIndexPaths([newIndexPath], withRowAnimation: .Bottom)
}
}
}
}
Looking at the code you provided there, I was going to suggest that putting your Realm writing logic in the view controller segue-controlled methods might be a little too late.
I would normally recommend that you should place any logic involving Realm writes in a method that is explicitly called from a user interacting with it. For example, in the tableView(_ tableView: UITableView,
didSelectRowAtIndexPath indexPath: NSIndexPath) method when they tap on a table cell, and then once the write has completed, perform the segue (or segue unwinding) then.
In any case, good to hear you solved it!

Resources