I have a UItableview in which I have cells that shows post of the users.
I want users to have the ability to delete their post with a "delete button" that is showed in their post.
I can do it but I want users to have a confirmation pop-up first when they click the delete button in the cell.
So I am adding the code below as an action in the "cell" file of the table, but I get an error which say "use of unresolved identifier presentviewcontroller".
Can I not use the presentviewcontroller inside a cell file?
#IBAction func button_clicked(sender: AnyObject) {
var refreshAlert = UIAlertController(title: "Refresh", message: "Do you want to delete this post?", preferredStyle: UIAlertControllerStyle.Alert)
refreshAlert.addAction(UIAlertAction(title: "Yes", style: .Default, handler: { (action: UIAlertAction!) in
println("Handle Ok logic here")
}))
refreshAlert.addAction(UIAlertAction(title: "No", style: .Default, handler: { (action: UIAlertAction!) in
println("Handle Cancel Logic here")
}))
presentViewController(refreshAlert, self, completion: nil)
}
Hmm, better use alert control in the view controller, because in controller, u can get every thing like tableview (say after deleting comment u have to reload), the data to be delete (something which is present in the (for example)array that u are used to display in tableview)... etc
first define a delegate method in the cell file for example
import UIKit
#objc protocol CellDelegate
{
func deleteCell(cell:CustomCommentCell)
}
class CustomCommentCell: UITableViewCell {
#IBOutlet weak var deleteButton: UIButton! //a delete button
weak var delegate:CellDelegate? //declare a delegate
override init(style: UITableViewCellStyle, reuseIdentifier: String?)
{
super.init(style: style, reuseIdentifier: reuseIdentifier)
}
required init(coder aDecoder: NSCoder)
{
super.init(coder: aDecoder)
}
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
//other code
//......
#IBAction func button_clicked(sender: AnyObject)
{
self.delegate?.deleteCell(self) //call the delegat method
}
in the ViewController
import UIKit
class ViewController: UIViewController,UITableViewDataSource,UITableViewDelegate, CellDelegate,UIAlertViewDelegate // add `CellDelegate`,UIAlertViewDelegate if u want t use alert view
{
//...other code
// ....
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
var cell:CustomCommentCell? = tableView.dequeueReusableCellWithIdentifier("CELL") as? CustomCommentCell;
if(cell == nil)
{
//..cell initilise
}
cell?.delegate = self //set the delegate to self
//..other code set up comment string .. etc
return cell!;
}
//define custom delegate method hear u can delete the cell
//since u are passing the cell so u can get the index path of the cell
func deleteCell(cell: CustomCommentCell)
{
var deleteCell:CustomCommentCell? = cell as CustomCommentCell
var indexPath: NSIndexPath = self.tableView.indexPathForCell(deleteCell!)! //get the index path
//using alert view
var alertToDelete: UIAlertView = UIAlertView(title: "Delete", message: "Are u sure to delete this comment", delegate: self, cancelButtonTitle: "Cancel", otherButtonTitles: "Ok")
alertToDelete.show()
/* uncomment if u want to use alertControl and comment above 2 line of alertView
//using alert control
var refreshAlert = UIAlertController(title: "Refresh", message: "Do you want to delete this post?", preferredStyle: UIAlertControllerStyle.Alert)
refreshAlert.addAction(UIAlertAction(title: "Yes", style: .Default, handler: { (action: UIAlertAction!) in
println("Handle Ok logic here")
//after deleting from datasource
self.tableView.reloadData()
}))
refreshAlert.addAction(UIAlertAction(title: "No", style: .Default, handler: { (action: UIAlertAction!) in
println("Handle Cancel Logic here")
}))
self.presentViewController(refreshAlert, animated: true, completion: nil)
*/
}
//suppose if u use alert view u will get delegate call back in this check which button is clicked
func alertView(alertView: UIAlertView, clickedButtonAtIndex buttonIndex: Int) {
if(buttonIndex == alertView.cancelButtonIndex)
{
//do nothing
println("Handle Cancel Logic here")
}
else
{
//delete hear
println("Handle Ok logic here")
//after deleting form datasource
self.tableView.reloadData()
}
}
The syntax is this:
self.presentViewController(<viewControllerToPresent: UIViewController>, animated: <Bool>, completion: <(() -> Void)?() -> Void>)
so, you need to do something like this:
self.presentViewController(refreshAlert, animated: true, completion: nil)
This is a solution I've used before - when the user swipes on the record in the table, they see the delete key. When selected, they get a pop-up that asks if they want to confirm the delete.
Lots of different ways to achieve this, probably better methods, but hope this helps someone.
In the view controller that contains the table, I included the following code:
// Code to handle delete records in tableview.
func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == .Delete {
let uiAlert = UIAlertController(title: "Delete Record", message: "Are you sure you want to delete this record?", preferredStyle: UIAlertControllerStyle.Alert)
self.presentViewController(uiAlert, animated: true, completion: nil)
uiAlert.addAction(UIAlertAction(title: "Yes", style: .Default, handler: { action in
//remove from data source
self.managedObjectContext.deleteObject(self.fetchedResults[indexPath.row] as! NSManagedObject)
do {
try self.managedObjectContext.save()
} catch _ {
}
// refresh table & header
self.fetchData()
}))
uiAlert.addAction(UIAlertAction(title: "Cancel", style: .Cancel, handler: nil))
}
}
I am not sure whether you can do it or not. But you should definitely not do it. Writing a 'controller' code inside a view class is most definitely not the way to do.
If I were you, I would do something like this.
Once the delete button in the row is pressed, a function in the UITableViewController should be called for the indexPath of the row.
This confirmationToDeleteIndexPath function of yours should present the refreshAlert and ask the user. On its callback you should attempt to delete the indexPath that was requested earlier.
let refreshAlert = UIAlertController(title: "Refresh", message: "Do you want to delete this post?", preferredStyle: UIAlertControllerStyle.alert)
refreshAlert.addAction(UIAlertAction(title: "Yes", style: .default, handler: { (action: UIAlertAction!) in
//perform your action
}))
refreshAlert.addAction(UIAlertAction(title: "No", style: .default, handler: { (action: UIAlertAction!) in
//perform your action
}))
present(refreshAlert, animated: true, completion: nil)
Related
I'm implementing leading/trailing swipe actions in my app.
The leading swipe action is to join/leave an event in the table. The trailing swipe action is to delete an event. Both of these swipe actions should be conditional, based primarily on if the user is logged in or not.
If the user swipes left or right, and the user is not logged in, I want to display an alert ("Login required...").
If the user is logged in, the leading action will conditionally be titled "Leave" or "Join" depending on if the user has already joined the event or not. The trailing "Delete" action will be created only if the user is the also the creator of the event.
When I test the app and the user is logged in, everything works perfectly. (That was working before I decided to add the conditional element.)
When I test the app, and the user is not logged in, the leading swipe works perfectly: I swipe left (in my case), the alert pops up. No swipe action appears in the TableViewCell.
The trailing swipe also shows the alert and reacts correctly, but for some reason it's also showing a "Delete" action, even though my code uses the title "Blah". After dismissing the alert, the red "Delete" action is still visible and clickable.
I've also completely removed the "trailingSwipe..." method but the "Delete" action still appears, so I need to figure out where the default is so I can turn it off and/or override it.
How do I prevent the default Delete action from appearing and display my action instead?
Here's my code for the leading swipe:
override func tableView(_ tableView: UITableView, leadingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
if currentUserID == nil {
showLoginRequiredMessage()
return nil
} else {
var userName = people.randomElement() // for testing
if event.eventMembers.contains(userName) {
let index = event.eventMembers.firstIndex(of: userName)!
let leaveAction = UIContextualAction(style: .normal, title: "Leave") { (action, view, nil) in
event.eventMembers.remove(at: index)
tableView.setEditing(false, animated: true)
tableView.reloadRows(at: [indexPath], with: .automatic)
self.saveEvents()
}
leaveAction.backgroundColor = .red
return UISwipeActionsConfiguration(actions: [leaveAction])
} else {
let joinAction = UIContextualAction(style: .normal, title: "Join") { (action, view, nil) in
event.eventMembers.append(userName)
tableView.setEditing(false, animated: true)
tableView.reloadRows(at: [indexPath], with: .automatic)
self.saveEvents()
}
joinAction.backgroundColor = .green
return UISwipeActionsConfiguration(actions: [joinAction])
}
}
}
Here's my code for the trailing swipe:
override func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
if currentUserID == nil {
showLoginRequiredMessage()
return nil
} else {
let trailingAction = UIContextualAction(style: .destructive, title: "Blah") { (action, view, nil) in
tableView.setEditing(false, animated: true)
print ("Delete this event")
}
trailingAction.backgroundColor = .red
return UISwipeActionsConfiguration(actions: [trailingAction])
}
}
And here's the code for the alert:
private func showLoginRequiredMessage() {
let ac = UIAlertController(title: "Login Required", message: "To modify an event, you must first login", preferredStyle: .alert)
ac.addAction(UIAlertAction(title: "Sign In", style: .default, handler: {(action) in
if let authenticationController = self.storyboard?.instantiateViewController(withIdentifier: "authenticationScreen") {
self.present(UINavigationController(rootViewController: authenticationController), animated: true)
}
}))
ac.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
present(ac, animated: true)
}
I have solved your issue. I hope that will work for you.
In trailingSwipeActions method change action style to normal, you will get "Blah" title.
Remove return nil from your if statement.
func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
if currentUserID == nil {
self.showLoginRequiredMessage()
}
let trailingAction = UIContextualAction(style: .normal, title: "Blah") { (action, view, boolval) in
print ("Custom action event")
tableView.setEditing(false, animated: true)
}
trailingAction.backgroundColor = .gray
return UISwipeActionsConfiguration(actions: [trailingAction])
}
And, add .setEditing(false, animated: true) in below method
private func showLoginRequiredMessage() {
let ac = UIAlertController(title: "Login Required", message: "To modify an event, you must first login", preferredStyle: .alert)
ac.addAction(UIAlertAction(title: "Sign In", style: .default, handler: {(action) in
self.myTableView.setEditing(false, animated: true)
if let authenticationController = self.storyboard?.instantiateViewController(withIdentifier: "authenticationScreen") {
self.present(UINavigationController(rootViewController: authenticationController), animated: true)
}
}))
ac.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: {(action) in
self.myTableView.setEditing(false, animated: true)
}))
present(ac, animated: true)
}
Based on ChillY's answer to this question (Why is the leading swipe action also duplicated as a trailing action?), I realized the problem was that I was returning nil instead of UISwipeActionsConfiguration(actions: []).
Now I just have to figure out why the swipes are not disappearing after the action has been executed. Any ideas?
I have found similar questions. None of which solve the same problem I am having.
This one here: Table view cell background goes white when deleting a cell - iOS
is in objective c. I need a swift answer.
I found this Change default background color while deleting a row from tableView
Answer in swift however when I add this to my code it still does not work.
Here is the code I am using to delete the cell.
override func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
let delete = UITableViewRowAction(style: .destructive, title: "delete") { (action, indexPath) in
let alertController = UIAlertController(title: "Warning", message: "Are you sure you want to delete this?", preferredStyle: .alert)
let deleteAction = UIAlertAction(title: "Delete", style: .destructive, handler: { (action) in
self.tableView.deleteRows(at: [indexPath], with: .fade)
})
alertController.addAction(deleteAction)
let cancelAction = UIAlertAction(title: "Cancel", style: .default, handler: nil)
alertController.addAction(cancelAction)
self.present(alertController, animated: true, completion: nil)
}
return [delete]
}
I need to maintain the color of the cell as the delete option comes from swiping.
Thanks!
Reload the table after deleting cell and also remove cell data from the array as well or use this line in view did load
tableView.tableFooterView = UIView(frame: .zero)
this will remove the empty cell form table
I have a tableview which uses two tableview row Actions one is delete and other is more.
When I tap on more button an alertcontroller is presented which has some actions.
I want that when morebutton is tapped the alertcontroller is presented but the rowActions remain there itself showing on which row the more button was pressed however it fades away as soon as I tap the more button.
Apple mail app and whatsapp do this successfully.
I am using xcode 9 and ios 11.1 in simulator.
I have used below code for implementing swipe to action functionality:
func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
let conversation = fetchedResultsController.object(at: indexPath)
let conversationId = Int(conversation.id)
let conversationType = Int(conversation.conversation_type_id)
let deleteTitle = getDeleteRowActionTitle(conversationId: conversationId, conversationType: conversationType)
let moreRowAction = UITableViewRowAction(style: UITableViewRowActionStyle.normal, title: "More", handler:{action, indexpath in
self.moreAction(conversation: conversation, conversationId:conversationId,conversationType:conversationType,indexPath:indexpath)
})
moreRowAction.backgroundColor = UIColor.green
let deleteRowAction = UITableViewRowAction(style: UITableViewRowActionStyle.destructive, title: deleteTitle, handler:{action, indexPath in
})
return [deleteRowAction, moreRowAction]
}
func moreAction(conversation:Nd_conversation,conversationId: Int,conversationType:Int,indexPath:IndexPath){
let muteTitle = notify ? "Mute" : "Unmute"
let moreActionSheet = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
let action1 = UIAlertAction(title: muteTitle, style: .default, handler: {(action) -> Void in
})
moreActionSheet.addAction(action1)
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: {(action) -> Void in })
moreActionSheet.addAction(cancelAction)
self.present(moreActionSheet, animated: true, completion: nil)
}
This is working in ios 10.3 but not in ios 11.
Alright, I am testing around a bit with core data, something I recently have started to discover, basically, what i have so far, is a single view app, that has a data source, and i can press a button and it brings up and alert, which from there i can add names to the list, and delete names from the list, i can close my app and still maintain my data. here is the issue/question, i am trying to do an update, so i can edit names in the list, i have a uibutton set up on my prototype cell, and i have it linked to my viewController, and have a function set inside the IBAction for the button. however, the button does not appear in my sim at run time.
here i have some code.
this is code for the edit button, and its function:
#IBAction func editButton(sender: AnyObject) {
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
// 2
let alert = UIAlertController(title: "Update",
message: "Please enter the new name.",
preferredStyle: .Alert)
// 3
let updateAction = UIAlertAction(title: "Save",
style: .Default){(_) in
let nameTextField = alert.textFields![0]
self.updateName(indexPath.row, newName: nameTextField.text!)
self.tableView.reloadData()
}
// 4
let cancelAction = UIAlertAction(title: "Cancel", style: .Cancel, handler: nil)
alert.addTextFieldWithConfigurationHandler(nil)
alert.addAction(updateAction)
alert.addAction(cancelAction)
// 5
self.presentViewController(alert, animated: true, completion: nil)
}
here is my cellForRowAtIndexPath func
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell")!
let people = peoples[indexPath.row]
cell.textLabel!.text = people.name
return cell
}
here is an image of my storyboard
Storyboard
if you need further information or code, please let me know and i will provide it.
I have a cell delete handle function that alerts a confirmation dialog, and if the user presses "Okay", it will go ahead and delete the cell. Otherwise, I want it to programmatically hide the delete button. I have the following function set up to handle deleting a UITableView cell in my UITableViewDelegate:
// delete a cell
func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if(editingStyle == .Delete) {
var deleteAlert = UIAlertController(
title: "Delete?",
message: "All data will be permanently deleted.",
preferredStyle: UIAlertControllerStyle.Alert)
deleteAlert.addAction(UIAlertAction(title: "Ok", style: .Default, handler: {
(action: UIAlertAction!) in
// delete logic here:
self.deleteDataForCell(indexPath.row)
self.myTable.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Automatic)
println("Delete successful")
}))
deleteAlert.addAction(UIAlertAction(title: "Cancel", style: .Default, handler: {
(action: UIAlertAction!) in
println("Delete cancelled")
// TODO - now hide the delete button with animation
}))
presentViewController(deleteAlert, animated: true, completion: nil)
}
}
The "TODO" part is where I don't know how to force the table to hide the delete button programatically. Right now the delete button will just stay in view until the user taps elsewhere on the screen. I can call myTable.reloadData() but that isn't animated.
Found a solution using this:
self.tableView.setEditing(false, animated: true)