Открыть программные кнопки редактирования 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]
}

Надеюсь, что это поможет.