Открыть программные кнопки редактирования UITableView программно
У меня есть UIPageViewController
, у которого UITableViewControllers
внутри него, а жестовые левые жесты конфликтуются между UIPageViewController
, чтобы изменить между представлениями и жестом UITableViewCells
, чтобы открыть действия редактирования, поэтому мне нужно показать действия редактирования, когда в ячейке нажата определенная кнопка.
Мой вопрос в том, можно ли программным образом отображать кнопки действия редактирования, а не показывать их по жестом салфетки?
Ответы
Ответ 1
У Apple есть частный API, который позволяет вам сделать это, однако, следует предупредить, что это может заставить ваше приложение отклонить приложение из App Store, если вы не запутали использование указанного API, используя что-то вроде метода Swizzling. Вот шаги для этого:
-
Создайте протокол с именем PrivateMethodRevealer
, который позволяет вам обращаться к необходимым частным API Apple, а именно к тем, чтобы показывать и отклонять действия редактирования. Кредиты на этот ответ для предоставления этого метода предоставления частных API. Методы в протоколе объявляются как optional
, так что в случае, если Apple изменит имя метода, приложение не будет разбиваться, но скорее оно просто не покажет действия редактирования.
@objc protocol PrivateMethodRevealer {
optional func setShowingDeleteConfirmation(arg1: Bool)
optional func _endSwipeToDeleteRowDidDelete(arg1: Bool)
}
Обратите внимание, что хотя методы относятся к delete
, это показывает все UITableViewRowAction
, которые находятся в ячейке.
-
Создайте функцию, которая обрабатывает показ и скрытие действий редактирования в вашем подклассе UITableViewCell
(если он у вас есть) или создайте метод в UITableViewCell
extension
. Я назову этот метод showActions
для демонстрационных целей.
-
Добавьте к своей функции следующее тело:
func showActions() {
(superview?.superview as? AnyObject)?._endSwipeToDeleteRowDidDelete?(false)
(self as AnyObject).setShowingDeleteConfirmation?(true)
}
Это сначала отменяет действия редактирования видимых ячеек, вызывая _endSwipeToDeleteRowDidDelete:
в UITableView
(который является супервизором супервизора), а затем показывает собственные действия редактирования ячейки (вызывая setShowingDeleteConfirmation:
). Обратите внимание, что нам нужно отклонить действия других ячеек, поскольку отображение нескольких строк с действиями редактирования крайне затруднительно.
-
Если вы хотите, вы также можете создать кнопку в UIViewController
, которая отклоняет любые редактируемые ячейки. Для этого просто вызовите следующий метод, где tableView
- это ссылка на UITableView
:
(tableView as AnyObject)?._endSwipeToDeleteRowDidDelete?(false)
Если жесты салфетки между вашими UIPageViewController
и UITableViewCell
конфликтуют, просто переопределите метод tableView:editingStyleForRowAtIndexPath:
, чтобы вернуть .None
.
В конце концов, ваш код может привести к следующему результату:
![Демо-видео]()
EDIT. Ниже приведен быстрый способ скрыть использование вашего API с помощью метода swizzling. Кредиты на этот веб-сайт для обеспечения базовой реализации этого метода. Будьте предупреждены, что я не могу гарантировать, что он сработает, поскольку это невозможно проверить в прямом эфире.
Чтобы сделать это, замените протоколы следующим кодом и везде, где вы вызываете setShowingDeleteConfirmation(true)
или _endSwipeToDeleteRowDidDelete(false)
, вместо этого замените его на showRowActions()
и hideRowActions()
. Однако этот метод имеет некоторые непреднамеренные эффекты, такие как UITableViewCell
не реагирует на взаимодействие пользователя, пока действия редактирования видны.
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)
}
}
}
Ответ 2
Я был на том же пути, что и kabiroberai, в поисках решения этого ответа, но использовал другой подход с двумя отдельными протоколами, а не с протоколом Objective-C/NSObject, который потенциально может быть использован неправильно. Это также предотвращает необходимость использования методов протокола.
Сначала создайте два отдельных протокола, чтобы открыть частные методы как для UITableView
, так и для UITableViewCell
. Я нашел их, выкапывая частные заголовки каждого класса.
@objc protocol UITableViewCellPrivate {
func setShowingDeleteConfirmation(arg1: Bool)
}
@objc protocol UITableViewPrivate {
func _endSwipeToDeleteRowDidDelete(arg1: Bool)
}
В cellForRowAtIndexPath
держите ссылку на ячейку (или несколько ячеек), которую вы хотите отобразить для действий редактирования:
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
}
}
Теперь запустите частные методы. Я использовал performSelector:withObject:afterDelay
, или вы можете использовать кнопку.
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)
}
}
Вызов unsafeBitCast
напрямую опасен. В целях безопасности проверьте, соответствуют ли ваши UITableView
и UITableViewCell
эти селекторы, или делают функции необязательными.
Ответ 3
В моем случае (swift 3, iOS11) MGSwipeTableCell работает отлично. Вы можете настроить кнопки вызова в
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
}
вместо
func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {...}
и поймать штрихи в
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
}
}
Ответ 4
В событии с нажатием кнопки вы можете попробовать с помощью функции ниже.
func setEditing(_ editing: Bool,
animated animated: Bool)
Это функция в синтаксисе swift.
В соответствии с сайтом поддержки разработчиков Apple он выполнит следующие действия:
Когда вы вызываете этот метод со значением редактирования, установленным в true, и объект UITableViewCell настроен на управление, ячейка показывает включение (зеленый плюс) или управление удалением (красный минус) на левой стороны каждой ячейки и регулятором переупорядочения с правой стороны. Этот метод вызывается для каждой видимой ячейки, когда setEditing: анимированный: вызывается метод UITableView. Вызов этого метод с редактированием, установленным на false, удаляет элементы управления из ячейки.
Надеюсь, что это ответит на вопрос.
Ответ 5
Задайте редактирование таблицы с помощью IBAction или другого метода:
@IBAction func editOn(sender: UIBarButtonItem) {
self.tableView.setEditing(true, animated: true)
}
Вы можете использовать методы из 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
}
И этот метод - действия для редактирования:
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]
}
Надеюсь, что это поможет.