Swipe to edit sqlite database content in swift - ios

Im trying to make an app that stores personal details which can be deleted, edited using editActionsForRowAtIndexPath. The delete option seems to work fine but I am having problems with the edit action.
I get an error as I've mentioned below:
Could not cast value of type 'Table_view.UserTableViewController' (0x10a1991b0) to 'NSIndexPath' (0x10a5e8438).
UserRecordViewController is the View Controller where the personal details are to be displayed. And InsertRecordViewController is the other View Controller.
UserTableViewController relevant Code :
func tableView(tableView: UITableView, editActionsForRowAtIndexPath indexPath: NSIndexPath) -> [UITableViewRowAction]? {
// 1
let editAction = UITableViewRowAction(style: UITableViewRowActionStyle.Default, title: "Edit" , handler: { (action:UITableViewRowAction, indexPath:NSIndexPath) -> Void in
self.performSegueWithIdentifier("editSegue", sender: self)
})
editAction.backgroundColor = UIColor.darkGrayColor()
// let editIndex = editAction.indexOfAccessibilityElement(indexPath.row)
let deleteAction = UITableViewRowAction(style: UITableViewRowActionStyle.Default, title: "Delete" , handler: { (action:UITableViewRowAction, indexPath:NSIndexPath) -> Void in
let userInfo: UserInfo = self.marrUserData.objectAtIndex(indexPath.row) as! UserInfo
let isDeleted = ModelManager.getInstance().deleteUserData(userInfo)
if isDeleted {
Util.invokeAlertMethod("", strBody: "Record deleted successfully.", delegate: nil)
} else {
Util.invokeAlertMethod("", strBody: "Error in deleting record.", delegate: nil)
}
self.getUserData()
})
deleteAction.backgroundColor = UIColor.lightGrayColor()
return [deleteAction, editAction]
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if(segue.identifier == "editSegue"){
let selectedIndexPath = sender as! NSIndexPath
let index = selectedIndexPath.row
//if let indexPath: NSIndexPath = self.tableView.indexPathForCell(sender as! UITableViewCell) {
//let btnEdit : UIButton = sender as! UIButton
//let selectedIndex : Int = btnEdit.tag
let viewController : InsertRecordViewController = segue.destinationViewController as! InsertRecordViewController
viewController.isEdit = true
viewController.userData = self.marrUserData.objectAtIndex(index) as! UserInfo
// }
}
}
I would like to know where I'm going wrong. Any idea guys?
Thanks in advance!!

I had the same exact problem and I was able to solve it by adding a few extra things in the func prepareForSegue
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if(segue.identifier == "editSegue"){
let cell = sender as! UITableViewCell
let Path = tableView.indexPathForCell(cell)
let index = Path?.row
let viewController : InsertRecordViewController = segue.destinationViewController as! InsertRecordViewController
viewController.isEdit = true
viewController.userData = self.marrUserData.objectAtIndex(index!) as! UserInfo
// }
}
Now I realize this isn't exactly what you're trying to do. Because with my method you just click on the cell and it directs you immideatly to the path instead of sliding. I wanted to do the slide as well but I couldn't figure it out so I just with with the above method. Hope this helps :)

The line that cause the error is: let selectedIndexPath = sender as! NSIndexPath
Sender is the SelectedCell, not the IndexPath!
Before the line self.performSegueWithIdentifier("editSegue", sender: self) set an property of the class (selectedIndexPath) to indexPath, and then you access this property from prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?).
Another method to do this can be found at: https://developer.apple.com/library/ios/referencelibrary/GettingStarted/DevelopiOSAppsSwift/Lesson9.html#//apple_ref/doc/uid/TP40015214-CH9-SW1

Related

Swift UISwipeActionsConfiguration and segue

I added leadingSwipeAction "Edit" button. However, when i press "Edit" button in simulator, app crashes and shows "Thread 1: signal SIGBART" in **prepare(for:sender:)**method.
I saw similar questions, but their solutions did't help. I'm newbie and can't understand where is the problem.
`//my edit button code`
override func tableView(_ tableView: UITableView, leadingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
let edit = UIContextualAction(style: .normal, title: "Edit") { [self] (contextualAction, view, actionPrformed: (Bool) -> Void) in
//TODO:
performSegue(withIdentifier: "EditItem", sender: self)
actionPrformed(true)
}
return UISwipeActionsConfiguration(actions: [edit])
}
// segue
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "AddItem" {
let controller = segue.destination as! AddAndEditItemViewController
controller.delegate = self
}
else if segue.identifier == "EditItem" {
let controller = segue.destination as! AddAndEditItemViewController
controller.delegate = self
if let indexPath = tableView.indexPath(
for: sender as! UITableViewCell) { //erorr shows here
controller.itemToEdit = items[indexPath.row]
}
}
}
self here
performSegue(withIdentifier: "EditItem", sender: self)
is the vc instance itself not the cell , you need to pass indexPath.row
performSegue(withIdentifier: "EditItem", sender: indexPath.row)
Then
let index = sender as! Int
controller.itemToEdit = items[index]
You can also use
guard let index = tableView.indexPathForSelectedRow?.row else { return }

UITableView swipe to edit

I have two uitableviewcontroller , one is lists table and the other is editing table.
I use accessory action to edit item. it works fine.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "EditItem" {
let controller = segue.destination as! ItemDetailViewController
controller.delegate = self
if let indexPath = tableView.indexPath(for: sender as! UITableViewCell) {
controller.itemToEdit = items[indexPath.row]
}
}
}
now I add swipe function to edit item
override func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
let edit = UIContextualAction(style: .normal, title: "Edit") { action, view, completion in
self.performSegue(withIdentifier: "EditItem", sender: self)
completion(true)
}
let delete = UIContextualAction(style: .destructive, title: "Delete") { [weak self] action, view, completion in
self?.items.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: UITableView.RowAnimation.automatic)
self?.saveChannelListItems()
completion(true)
}
edit.backgroundColor = .purple
edit.image = #imageLiteral(resourceName: "edit")
delete.backgroundColor = .red
delete.image = #imageLiteral(resourceName: "delete")
return UISwipeActionsConfiguration(actions: [delete, edit])
}
then I have errors.
Could not cast value of type 'abc.ListController' (0x1089b1398) to 'UITableViewCell' (0x1150f18e0).
how can I swipe to edit items?
Here you send self which is the vc in sender parameter
self.performSegue(withIdentifier: "EditItem", sender: self)
and force unwrap it to the cell which is the problem
if let indexPath = tableView.indexPath(for: sender as! UITableViewCell) {
Instead you can do
self.performSegue(withIdentifier: "EditItem", sender:items[indexPath.row])
with
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "EditItem" {
let controller = segue.destination as! ItemDetailViewController
controller.delegate = self
controller.itemToEdit = sender as! ItemType // where ItemType is the type of the array elements
}
}
}

IOS present Viewcontroller and pass data inside editActionsForRowAt

Hi I'm developing an app with custom action in tableview cell.
when the user swipe the cell. There will be 2 action: delete and more. I want more action to present a new viewController and pass the object data to the new viewController. How do we achieve that?
I have tried using this
in viewDidLoad
if let split = self.splitViewController {
let controllers = split.viewControllers
self.detailViewController = (controllers[controllers.count-1] as! UINavigationController).topViewController as? DetailViewController
}
and editActionsForRowAt method
override func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
let moreRowAction = UITableViewRowAction(style: .normal, title: "More", handler:{action, indexpath in
if let indexPath = self.tableView.indexPathForSelectedRow {
let object = self.fetchedResultsController.object(at: indexPath)
let controller = self.detailViewController!
controller.detailItem = object
controller.navigationItem.leftBarButtonItem = self.splitViewController?.displayModeButtonItem
controller.navigationItem.leftItemsSupplementBackButton = true
self.present(controller, animated: true, completion: nil)
}
// self.performSegue(withIdentifier: "showDetail", sender: self)
});
moreRowAction.backgroundColor = UIColor(red: 0.298, green: 0.851, blue: 0.3922, alpha: 1.0);
let deleteRowAction = UITableViewRowAction(style: .destructive, title: "Delete", handler:{action, indexpath in
self.deleteTapped(indexPath: indexPath as NSIndexPath)
});
return [deleteRowAction, moreRowAction];
}
The delete function work but the more action does not work. And help is much appreciate! Thanks
My prepareforseuge
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showDetail" {
if let indexPath = self.tableView.indexPathForSelectedRow {
let object = self.fetchedResultsController.object(at: indexPath)
let controller = (segue.destination as! UINavigationController).topViewController as! DetailViewController
controller.detailItem = object
controller.navigationItem.leftBarButtonItem = self.splitViewController?.displayModeButtonItem
controller.navigationItem.leftItemsSupplementBackButton = true
}
}
}
You can pass your object via the sender parameter to your segue:
let moreRowAction = UITableViewRowAction(style: .normal, title: "More", handler:{action, indexpath in
let object = self.fetchedResultsController.object(at: indexPath)
self.performSegue(withIdentifier: "showDetail", sender: object)
});
Then in prepare(for segue: sender:) -
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showDetail" {
guard let navVC = segue.destination as? UINavigationController,
let destVC = navVC.viewControllers.first as? DetailViewController else {
return
}
var object = sender as? Event
if object == nil {
if let indexPath = self.tableView.indexPathForSelectedRow {
object = self.fetchedResultsController.object(at: indexPath)
}
}
destVC.detailItem = object
}
}
The Master-Detail template project uses a navigation controller as the detail destination, so the prepare(for segue) function needs to access the navigation controller's first view controller.
Also, since you are using the same segue for row tap and "more" actions, my code checks for both possibilities - the detail item could be in the sender or in the selected row

UIImage not appearing

When I segue my UIImageView from my NewsTableViewController.swift, the image does not appear in my NewsDetailTableViewController.swift.
Here is an image of my simulator:
Here is an image of my Main.storyboard:
Here is my prepareForSegue() method code:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?){
if segue.identifier == "showDetail" {
if let indexPath = tableView.indexPathForSelectedRow {
let destinationController = segue.destinationViewController as! NewsDetailTableViewController
destinationController.item = items[indexPath.row]
let newsImage = UIImageView(image: UIImage(named: detailImages[indexPath.row]))
destinationController.image = newsImage
}
}
}
It is also worth mentioning that my image in my NewsTableViewController.swift is set to 0x0, and in my NewsDetailTableViewController.swift, it is set to 600x216. I don't know if this would matter or not. If you desire any code, please do feel free to ask.
FYI, I use Swift.
In prepareForSegue the IBOutlets of your destinationController have not been initialized yet. Just pass detailImages to another array property on NewsDetailTableViewController. Change your prepareForSegue to:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?){
if segue.identifier == "showDetail" {
if let indexPath = tableView.indexPathForSelectedRow {
let destinationController = segue.destinationViewController as! NewsDetailTableViewController
destinationController.item = items[indexPath.row]
destinationController.imageNames = detailImages
}
}
}
Then in NewsTableViewController add the following code:
var imageNames:[String]?
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
//replace with whatever identifier you're using
var cell:UITableViewCell = self.tableView.dequeueReusableCellWithIdentifier("Cell") as UITableViewCell
cell.imageView?.image = self.imageNames[indexPath.row]?
return cell
}

Table row action to segue with indexPath row

So I have a table view and a Custom VC.
then I have an object exercises with detailImage as property.
How can I get the indexPath.row from the table row action into my prepareForSegue function?
this returns nil: self.tableView.indexPathForSelectedRow
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "detailsSegue" {
if let indexPath = self.tableView.indexPathForSelectedRow {
let destinationController = segue.destinationViewController as! DetailViewController
print(self.exercises[indexPath.row].image)
destinationController.detailImage = self.exercises[indexPath.row].image
print ("send")
}
}
}
Instead of this in your code:
if let indexPath = self.tableView.indexPathForSelectedRow {
Try this:
if let indexPath = self.tableView.indexPathForSelectedRow() {
After playing around with the sender object in editActionsForRowAtIndexPath
I got it to work. It's maybe a dirty solution but it works.
override func tableView(tableView: UITableView, editActionsForRowAtIndexPath indexPath: NSIndexPath) -> [UITableViewRowAction]? {
let detailsAction = UITableViewRowAction(style: .Default, title: "Details", handler: {(action:UITableViewRowAction!, indexPath:NSIndexPath!) -> Void in
self.performSegueWithIdentifier("detailsSegue", sender: indexPath) //sender is the indexPath
}
)
and then in the prepareForSegue
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "detailsSegue" {
let indexPath = sender!
let destinationController = segue.destinationViewController as! DetailViewController
print(self.exercises[indexPath.row].image)
There is probably a better way..Anyone to comment?

Resources