以编程方式打开 UITableView 编辑操作按钮
Posted
技术标签:
【中文标题】以编程方式打开 UITableView 编辑操作按钮【英文标题】:Open UITableView edit action buttons programmatically 【发布时间】:2015-07-21 15:48:15 【问题描述】:我有一个UIPageViewController
,里面有UITableViewControllers
,并且向左滑动手势在UIPageViewController
之间发生冲突以在视图之间切换,而UITableViewCells
手势则用于打开编辑操作,所以我需要在单元格中单击某个按钮时显示编辑操作。
我的问题是我可以以编程方式显示编辑操作按钮,而不是在滑动手势上显示它们吗?
【问题讨论】:
我也遇到了同样的问题,请问您有解决方法吗?请与我们分享。 @eng_rawan 实际上没有这个问题仍然悬而未决。 您找到解决方案了吗? @Firas 据我所知,UITableView
没有公开任何方法来做到这一点,所以你最好的选择可能是只使用像 MGSwipeTableCell 这样的框架或遵循像 @ 这样的教程987654322@.
更新:我认为我已经找到了启动滑动的(私有)函数,我可能能够在几个我有时间的几个小时
【参考方案1】:
Apple 有一个私有 API 可以让您执行此操作,但是请注意,这可能会导致您的应用被 App Store 拒绝,除非您使用类似 Method Swizzling 之类的方法混淆了所述 API 的使用。以下是执行此操作的步骤:
创建一个名为 PrivateMethodRevealer
的协议,让您可以访问所需的私有 Apple API,即显示和关闭编辑操作的那些。感谢 this answer 提供了这种公开私有 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)
这首先通过在UITableView
(这是单元格的超级视图的超级视图)上调用_endSwipeToDeleteRowDidDelete:
来消除任何可见单元格的编辑操作,然后显示单元格自己的编辑操作(通过调用setShowingDeleteConfirmation:
)。请注意,我们需要关闭其他单元格的操作,因为显示多行的编辑操作非常有问题。
如果您愿意,您还可以在UIViewController
中创建一个按钮,用于关闭任何当前正在编辑的单元格。为此,只需调用以下方法,其中tableView
是您对UITableView
的引用:
(tableView as AnyObject)?._endSwipeToDeleteRowDidDelete?(false)
如果您的UIPageViewController
和UITableViewCell
s 之间的滑动手势冲突,只需覆盖tableView:editingStyleForRowAtIndexPath:
方法以返回.None
。
最后,您的代码可能会产生以下结果
编辑:这是一种使用方法调配来隐藏 API 使用的快速方法。感谢 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)
【讨论】:
好答案,比我快一天。很好地参考了我的问题。我不建议使用NSObject
/AnyObject
符合的一种全局协议。考虑两个单独的协议并使用unsafeBitCast
,如my answer 所示。
@JAL 虽然使用一种协议看起来很危险,但optional
协议的美妙之处在于,如果对象不符合协议,它就会默默地失败。这也可能发生在方法名称被更改的情况下,因此它用一块石头杀死了两只鸟(即您不需要调用respondsToSelector:
)。
@JAL,如果您看到我链接的答案,它说明unsafeBitCast
s 本质上是不安全。如果由于某种原因该方法不存在,应用程序将崩溃。
我当然看了答案,这是我的问题。是的,所有这些都是不安全的,但我们知道这些方法是由类实现的,所以unsafeBitCast
没有任何危害。如果您想更安全,请使用可选协议并检查选择器。对于小型原型设计,unsafeBitCast
就足够了。
@JAL 哦,对不起,我不知何故错过了您提出链接问题的事实 xD【参考方案2】:
我与 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
是否响应这些选择器,或将这些功能设为可选。
【讨论】:
我很确定_endSwipeToDeleteRowDidDelete:
中的参数是指该行是否被删除,而不是它是否应该结束滑动。
在 Xcode 9 / ios 10 中不适合我,你能确认它适合你吗?【参考方案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】:使用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]
希望对你有帮助。
【讨论】:
这会添加动作,但不显示如何以编程方式使它们可见。【参考方案5】:在您的按钮点击事件中,请您尝试以下功能。
func setEditing(_ editing: Bool,
animated animated: Bool)
这是函数在 swift 语法中。
根据Apple developer support 网站,它将执行以下操作,
当您调用此方法并将editing的值设置为true时,并且 UITableViewCell 对象被配置为具有控件,单元格 显示插入(绿色加号)或删除控制(红色减号) 每个单元格的左侧和右侧的重新排序控件。 此方法在每个可见单元格上调用,当 setEditing:animated: UITableView 的方法被调用。调用这个 编辑设置为 false 的方法从单元格中删除控件。
希望这能回答问题。
【讨论】:
我确实尝试过该功能,但不幸的是它不起作用。以上是关于以编程方式打开 UITableView 编辑操作按钮的主要内容,如果未能解决你的问题,请参考以下文章
在 UITableViewController 中以编程方式切换 UITableView
以编程方式编辑 UITableView 在所有行上显示删除按钮