I have a UIPageViewController
that have UITableViewControllers
inside it, and the swipe left gestures are conflicted between the UIPageViewCo
Set the table view Editing with an IBAction or other method:
@IBAction func editOn(sender: UIBarButtonItem) {
self.tableView.setEditing(true, animated: true)
}
You can use the methods from UITableViewController:
// 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
}
And this method is the actions for editing:
override func tableView(tableView: UITableView, editActionsForRowAtIndexPath indexPath: NSIndexPath) -> [UITableViewRowAction]? {
let action = UITableViewRowAction(style: .Default, title: "Delete") { (rowAction: UITableViewRowAction, indexPAth: NSIndexPath) in
//code for delete
print("Delete")
}
let action2 = UITableViewRowAction(style: .Normal, title: "Share") { (rowAction: UITableViewRowAction, indexPAth: NSIndexPath) in
//code for share
print("Share")
}
return [action, action2]
}
Hope it helps.
On your button click event, could you please try with below function.
func setEditing(_ editing: Bool,
animated animated: Bool)
This is function is in the swift syntax.
As per the Apple developer support website it will do the following things,
When you call this method with the value of editing set to true, and the UITableViewCell object is configured to have controls, the cell shows an insertion (green plus) or deletion control (red minus) on the left side of each cell and a reordering control on the right side. This method is called on each visible cell when the setEditing:animated: method of UITableView is invoked. Calling this method with editing set to false removes the controls from the cell.
Hope this answers the question.
I was on the same path as kabiroberai in finding a solution to this answer, but took a different approach with two separate protocols rather than one Objective-C/NSObject protocol that could potentially be misused. This also prevents having to make the protocol methods optional.
First, create two separate protocols to expose the private methods on both the UITableView
and UITableViewCell
. I found these by digging through the private headers of each class.
@objc protocol UITableViewCellPrivate {
func setShowingDeleteConfirmation(arg1: Bool)
}
@objc protocol UITableViewPrivate {
func _endSwipeToDeleteRowDidDelete(arg1: Bool)
}
In cellForRowAtIndexPath
, keep a reference to the cell (or multiple cells) which you want to show the edit actions for:
class MyTableViewController: UITableViewController {
var cell: UITableViewCell?
// ...
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell")!
// ...
if indexPath.row == 1 {
self.cell = cell
}
return cell
}
}
Now, fire the private methods. I used performSelector:withObject:afterDelay
, or you can use a button.
override func viewDidLoad() {
super.viewDidLoad()
self.performSelector(#selector(showActionsForCell), withObject: nil, afterDelay: 2.0)
}
func showActionsForCell() {
if let cell = cell {
let cellPrivate = unsafeBitCast(cell, UITableViewCellPrivate.self)
let tableViewPrivate = unsafeBitCast(self.tableView, UITableViewPrivate.self)
// Dismiss any other edit actions that are open
tableViewPrivate._endSwipeToDeleteRowDidDelete(false)
// Open the edit actions for the selected cell
cellPrivate.setShowingDeleteConfirmation(true)
}
}
Calling unsafeBitCast
directly is dangerous. For safety, check if your UITableView
and UITableViewCell
respond to those selectors, or make the functions optional.
In my case (swift 3, iOS11) MGSwipeTableCell works perfect. You can configure call's buttons in
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: prettyIdentifier, for: indexPath)
cell.allowsButtonsWithDifferentWidth = true
cell.rightButtons = [MGSwipeButton(title: "Delete\npermanently", backgroundColor: #colorLiteral(red: 0.9745360017, green: 0.7205639482, blue: 0.3932176828, alpha: 1)), MGSwipeButton(title: "Undo",backgroundColor: .black)]
cell.rightSwipeSettings.transition = .rotate3D
cell.delegate = self
return cell
}
instead of
func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {...}
and catch touches in
extension RecordingViewController: MGSwipeTableCellDelegate {
func swipeTableCell(_ cell: MGSwipeTableCell, tappedButtonAt index: Int, direction: MGSwipeDirection, fromExpansion: Bool) -> Bool {
cell.hideSwipe(animated: true)
// do your stuff here like
if index == 0 {
print("right button")
}
return true
}
}
Apple has a private API that lets you do this, however, be warned that this may get your app rejected from the App Store unless you obfuscate the usage of said API using something like Method Swizzling. Here are the steps to do so:
Create a protocol called PrivateMethodRevealer
that lets you access the required private Apple APIs, namely the ones to show and dismiss edit actions. Credits to this answer for providing this method of exposing private APIs. The methods in the protocol are declared as optional
, so that in case Apple changes the name of the method, the app will not crash, but rather, it'll just not show the edit actions.
@objc protocol PrivateMethodRevealer {
optional func setShowingDeleteConfirmation(arg1: Bool)
optional func _endSwipeToDeleteRowDidDelete(arg1: Bool)
}
Note that although the methods refer to delete
, this shows all the UITableViewRowAction
s that are on the cell.
Create a function that handles the showing and hiding of the edit actions in your UITableViewCell
subclass (if you have one), or create the method in a UITableViewCell
extension
. I will name this method showActions
for demonstrative purposes.
Add the following body to your function:
func showActions() {
(superview?.superview as? AnyObject)?._endSwipeToDeleteRowDidDelete?(false)
(self as AnyObject).setShowingDeleteConfirmation?(true)
}
This firstly dismisses any visible cells' editing actions, by calling _endSwipeToDeleteRowDidDelete:
on the UITableView
(which is the cell's superview's superview), and then shows the cell's own editing actions (by calling setShowingDeleteConfirmation:
). Note that we need to dismiss other cells' actions as showing multiple rows with edit actions is extremely buggy.
If you want, you may also create a button in the UIViewController
that dismisses any currently editing cells. To do this, just call the following method, where tableView
is your reference to the UITableView
:
(tableView as AnyObject)?._endSwipeToDeleteRowDidDelete?(false)
If the swipe gestures between your UIPageViewController
and UITableViewCell
s are conflicting, simply override the tableView:editingStyleForRowAtIndexPath:
method to return .None
.
In the end, your code might produce the following result
EDIT: Here is a quick way to hide the usage of your API using method swizzling. Credits to this website for providing the basic implementation of this method. Be warned that I can't guarantee that it'll work, as it isn't possible to test it live.
To do this, replace the protocols with the following code, and wherever you call setShowingDeleteConfirmation(true)
or _endSwipeToDeleteRowDidDelete(false)
, replace it with showRowActions()
and hideRowActions()
instead. This method appears to have some unintended effects however, such as the UITableViewCell
s not responding to user interaction whilst edit actions are visible.
extension UITableViewCell {
func showRowActions(arg1: Bool = true) {}
public override static func initialize() {
struct Static {
static var token: dispatch_once_t = 0
}
guard self === UITableViewCell.self else {return}
dispatch_once(&Static.token) {
let hiddenString = String(":noitamrifnoCeteleDgniwohStes".characters.reverse())
let originalSelector = NSSelectorFromString(hiddenString)
let swizzledSelector = #selector(showRowActions(_:))
let originalMethod = class_getInstanceMethod(self, originalSelector)
let swizzledMethod = class_getInstanceMethod(self, swizzledSelector)
class_addMethod(self, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod))
method_exchangeImplementations(originalMethod, swizzledMethod)
}
}
}
extension UITableView {
func hideRowActions(arg1: Bool = false) {}
public override static func initialize() {
struct Static {
static var token: dispatch_once_t = 0
}
guard self === UITableView.self else {return}
dispatch_once(&Static.token) {
let hiddenString = String(":eteleDdiDwoReteleDoTepiwSdne_".characters.reverse())
let originalSelector = NSSelectorFromString(hiddenString)
let swizzledSelector = #selector(hideRowActions(_:))
let originalMethod = class_getInstanceMethod(self, originalSelector)
let swizzledMethod = class_getInstanceMethod(self, swizzledSelector)
class_addMethod(self, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod))
method_exchangeImplementations(originalMethod, swizzledMethod)
}
}
}