2015-07-21 3 views
33

У меня есть UIPageViewController что есть UITableViewControllers внутри него, и проведите пальцем влево жесты противоречили между UIPageViewController изменения между видом и UITableViewCells жест, чтобы открыть редактировать действия, так что мне нужно, чтобы показать редактировать действия, когда в ячейке нажата определенная кнопка.кнопки редактирования действий Открыть UITableView программно

Мой вопрос в том, можно ли программным образом отображать кнопки действия действия, а не показывать их на жесте салфетки?

+0

Я тоже столкнулся с той же проблемой, вы получили какое-либо решение? , пожалуйста, поделитесь им с нами. – Rawan

+0

@eng_rawan. Эта проблема еще не решена. – Firas

+0

Вы нашли решение? @Firas –

ответ

9

У Apple есть частный API, который позволяет вам сделать это, однако, следует предупредить, что это может привести к тому, что ваше приложение будет отклонено из App Store, если вы не запутаете использование указанного API, используя что-то вроде Метод Swizzling. Вот шаги, чтобы сделать так:

  1. Создать протокол под названием PrivateMethodRevealer, который позволяет получить доступ к необходимой частному API, Apple, а именно те, чтобы показать и увольняют редактировать действия. Кредиты на this answer за предоставление этого метода предоставления частных API. Методы в протоколе объявляются как optional, так что в случае, если Apple изменит имя метода, приложение не будет разбиваться, но скорее оно просто не покажет действия редактирования.

    @objc protocol PrivateMethodRevealer { 
        optional func setShowingDeleteConfirmation(arg1: Bool) 
        optional func _endSwipeToDeleteRowDidDelete(arg1: Bool) 
    } 
    

    Следует отметить, что хотя методы относятся к delete, это показывает все UITableViewRowAction S, которые находятся на клетки.

  2. Создайте функцию, которая обрабатывает показ и скрытие редактирования действий в вашем UITableViewCell подкласса (если у вас есть), или создать метод в UITableViewCellextension. Я назову этот метод showActions в демонстрационных целях.

  3. Добавьте следующее тело к вашей функции:

    func showActions() { 
        (superview?.superview as? AnyObject)?._endSwipeToDeleteRowDidDelete?(false) 
        (self as AnyObject).setShowingDeleteConfirmation?(true) 
    } 
    

    Это, во-первых увольняет действия редактирования любых видимых ячеек, позвонив по телефону _endSwipeToDeleteRowDidDelete: на UITableView (который SuperView SuperView в клетки), а затем показывает клетки собственные действия по редактированию (по телефону setShowingDeleteConfirmation:). Обратите внимание, что нам нужно отклонить действия других ячеек, поскольку отображение нескольких строк с действиями редактирования крайне затруднительно.

  4. Если вы хотите, вы также можете создать кнопку в UIViewController, которая отклоняет любые редактируемые ячейки. Чтобы сделать это, просто вызовите следующий метод, где tableView ваша ссылка на UITableView:

    (tableView as AnyObject)?._endSwipeToDeleteRowDidDelete?(false) 
    

Если салфетки жесты между вашими UIPageViewController и UITableViewCell с конфликтуют, просто переопределить метод tableView:editingStyleForRowAtIndexPath: вернуть .None ,

В конце концов, ваш код может произвести следующий результат Demo video

EDIT: Вот быстрый способ, чтобы скрыть использование вашего API с помощью метода swizzling. Кредиты для this website для обеспечения базовой реализации этого метода. Будьте предупреждены, что я не могу гарантировать, что он будет работать, так как это невозможно проверить в прямом эфире.

Для этого замените протоколы со следующим кодом, и везде, где вы звоните setShowingDeleteConfirmation(true) или _endSwipeToDeleteRowDidDelete(false), замените его showRowActions() и hideRowActions() вместо этого. Однако этот метод имеет некоторые непреднамеренные эффекты, например, UITableViewCell s не реагирует на взаимодействие пользователя, пока действия редактирования видны.

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) 
     } 
    } 
} 
+0

Хороший ответ, избили меня на день. Хорошая ссылка на мой вопрос тоже. Я бы не рекомендовал, чтобы один глобальный протокол соответствовал требованиям NSObject'/'AnyObject'. Рассмотрим два отдельных протокола и использование 'unsafeBitCast', как показано в [моем ответе] (http://stackoverflow.com/a/39147692/2415822). – JAL

+0

@JAL, хотя может показаться опасным использовать один протокол, красота «необязательных» протоколов заключается в том, что если объект не соответствует протоколу, он просто терпит неудачу. Это может также произойти в случае названия изменяемого метода, поэтому он убивает двух зайцев одним камнем (т. Е. Вам не нужно вызывать 'responsesToSelector:'). – kabiroberai

+0

@JAL также, если вы видите ответ, который я связал, он объясняет, что 'unsafeBitCast' по своей сути _unsafe_. Если по какой-либо причине метод не существует, приложение выйдет из строя. – kabiroberai

-3

На вашем мероприятии нажмите кнопку мыши, не могли бы вы попробовать использовать функцию ниже.

func setEditing(_ editing: Bool, 
    animated animated: Bool) 

Это функция в оперативном синтаксисе.

В соответствии с Apple developer support сайта он будет делать следующие вещи,

При вызове этого метода со значением редактирования значение ИСТИНА, и объект UITableViewCell сконфигурирован, чтобы иметь контроль, клетки показывает вставку (зеленый плюс) или управление удалением (красный минус) на левой стороне каждой ячейки и регуляторе переупорядочения с правой стороны. Этот метод вызывается для каждой видимой ячейки, когда вызывается метод setEditing: анимированный: UITableView. Вызов этого метода с редактированием, установленным на false, удаляет элементы управления из ячейки.

Надеюсь, что это ответит на вопрос.

+2

Я пробовал эту функцию, но, к сожалению, она не работала. – Firas

-2

Установить вид таблицы Редактирование с 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] 
} 

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

+1

Это добавляет действия, но не показывает, как сделать их видимыми программно. – JAL

0

Я был на том же пути, kabiroberai в поиске решения этого ответа, но взял другой подход с двумя отдельными протоколами, а не один Objective-C протокол/NSObject, которые потенциально могут быть неправильно. Это также предотвращает необходимость использования методов протокола.

Сначала создайте два отдельных протокола, чтобы открыть частные методы как для UITableView, так и для UITableViewCell. Я нашел их, пробираясь через private headers каждого класса.

@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 этим селекторам или делают эти функции необязательными.

+0

Я уверен, что аргумент в '_endSwipeToDeleteRowDidDelete:' относится к тому, была ли строка удалена, а не должна ли она заканчиваться. – kabiroberai

+0

Не работает для меня в Xcode 9/iOS 10, можете ли вы подтвердить, что он работает для вас? –

0

В моем случае (быстрый 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 
    } 
} 
Смежные вопросы