我希望能够使用 longPress 手势(而不是标准的重新排序控件)重新排序 tableview 单元格。识别出 longPress 后,我希望 tableView 基本上进入“编辑模式”,然后重新排序,就好像我正在使用 Apple 提供的重新排序控件一样。
有没有办法做到这一点而无需依赖第 3 方解决方案?
提前致谢。
编辑:我最终使用了已接受答案中的解决方案,并依赖于第 3 方解决方案。
我希望能够使用 longPress 手势(而不是标准的重新排序控件)重新排序 tableview 单元格。识别出 longPress 后,我希望 tableView 基本上进入“编辑模式”,然后重新排序,就好像我正在使用 Apple 提供的重新排序控件一样。
有没有办法做到这一点而无需依赖第 3 方解决方案?
提前致谢。
编辑:我最终使用了已接受答案中的解决方案,并依赖于第 3 方解决方案。
他们在 iOS 11 中添加了一种方式。
首先,启用拖放交互并设置拖放代理。
然后实现 moveRowAt,就好像您使用重新排序控件正常移动单元格一样。
然后实现拖放委托,如下所示。
tableView.dragInteractionEnabled = true
tableView.dragDelegate = self
tableView.dropDelegate = self
func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) { }
extension TableView: UITableViewDragDelegate {
func tableView(_ tableView: UITableView, itemsForBeginning session: UIDragSession, at indexPath: IndexPath) -> [UIDragItem] {
return [UIDragItem(itemProvider: NSItemProvider())]
}
}
extension TableView: UITableViewDropDelegate {
func tableView(_ tableView: UITableView, dropSessionDidUpdate session: UIDropSession, withDestinationIndexPath destinationIndexPath: IndexPath?) -> UITableViewDropProposal {
if session.localDragSession != nil { // Drag originated from the same app.
return UITableViewDropProposal(operation: .move, intent: .insertAtDestinationIndexPath)
}
return UITableViewDropProposal(operation: .cancel, intent: .unspecified)
}
func tableView(_ tableView: UITableView, performDropWith coordinator: UITableViewDropCoordinator) {
}
}
Swift 3 并且没有第三方解决方案
首先,将这两个变量添加到您的类中:
var dragInitialIndexPath: IndexPath?
var dragCellSnapshot: UIView?
然后添加UILongPressGestureRecognizer
到您的tableView
:
let longPress = UILongPressGestureRecognizer(target: self, action: #selector(onLongPressGesture(sender:)))
longPress.minimumPressDuration = 0.2 // optional
tableView.addGestureRecognizer(longPress)
手柄UILongPressGestureRecognizer
:
// MARK: cell reorder / long press
func onLongPressGesture(sender: UILongPressGestureRecognizer) {
let locationInView = sender.location(in: tableView)
let indexPath = tableView.indexPathForRow(at: locationInView)
if sender.state == .began {
if indexPath != nil {
dragInitialIndexPath = indexPath
let cell = tableView.cellForRow(at: indexPath!)
dragCellSnapshot = snapshotOfCell(inputView: cell!)
var center = cell?.center
dragCellSnapshot?.center = center!
dragCellSnapshot?.alpha = 0.0
tableView.addSubview(dragCellSnapshot!)
UIView.animate(withDuration: 0.25, animations: { () -> Void in
center?.y = locationInView.y
self.dragCellSnapshot?.center = center!
self.dragCellSnapshot?.transform = (self.dragCellSnapshot?.transform.scaledBy(x: 1.05, y: 1.05))!
self.dragCellSnapshot?.alpha = 0.99
cell?.alpha = 0.0
}, completion: { (finished) -> Void in
if finished {
cell?.isHidden = true
}
})
}
} else if sender.state == .changed && dragInitialIndexPath != nil {
var center = dragCellSnapshot?.center
center?.y = locationInView.y
dragCellSnapshot?.center = center!
// to lock dragging to same section add: "&& indexPath?.section == dragInitialIndexPath?.section" to the if below
if indexPath != nil && indexPath != dragInitialIndexPath {
// update your data model
let dataToMove = data[dragInitialIndexPath!.row]
data.remove(at: dragInitialIndexPath!.row)
data.insert(dataToMove, at: indexPath!.row)
tableView.moveRow(at: dragInitialIndexPath!, to: indexPath!)
dragInitialIndexPath = indexPath
}
} else if sender.state == .ended && dragInitialIndexPath != nil {
let cell = tableView.cellForRow(at: dragInitialIndexPath!)
cell?.isHidden = false
cell?.alpha = 0.0
UIView.animate(withDuration: 0.25, animations: { () -> Void in
self.dragCellSnapshot?.center = (cell?.center)!
self.dragCellSnapshot?.transform = CGAffineTransform.identity
self.dragCellSnapshot?.alpha = 0.0
cell?.alpha = 1.0
}, completion: { (finished) -> Void in
if finished {
self.dragInitialIndexPath = nil
self.dragCellSnapshot?.removeFromSuperview()
self.dragCellSnapshot = nil
}
})
}
}
func snapshotOfCell(inputView: UIView) -> UIView {
UIGraphicsBeginImageContextWithOptions(inputView.bounds.size, false, 0.0)
inputView.layer.render(in: UIGraphicsGetCurrentContext()!)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
let cellSnapshot = UIImageView(image: image)
cellSnapshot.layer.masksToBounds = false
cellSnapshot.layer.cornerRadius = 0.0
cellSnapshot.layer.shadowOffset = CGSize(width: -5.0, height: 0.0)
cellSnapshot.layer.shadowRadius = 5.0
cellSnapshot.layer.shadowOpacity = 0.4
return cellSnapshot
}
除非您想从头开始将您自己的 UITableView + Controller 组合在一起,否则您无法使用 iOS SDK 工具来做到这一点,这需要大量的工作。您提到不依赖 3rd 方解决方案,但我的自定义 UITableView 类可以很好地处理这个问题。随意检查一下:
所以本质上你想要“清除”式的行重新排序对吗?(大约 0:15)
不幸的是,我认为您无法使用当前的 iOS SDK 工具来做到这一点,除非您从头开始将 UITableView + Controller 组合在一起(您需要自己创建每一行并让 UITouch 响应与您的 row-to 的 CGRect 相关的-移动)。
这会非常复杂,因为当您移动要重新排序的行时,您需要让行的动画“让开”。
cocoas 工具看起来很有前途,至少去看看源代码。
现在有一个很棒的 Swift 库,现在称为SwiftReorder
MIT 许可,因此您可以将其用作第一方解决方案。这个库的基础是它使用UITableView
扩展将控制器对象注入到任何符合以下条件的表视图中TableViewReorderDelegate
:
extension UITableView {
private struct AssociatedKeys {
static var reorderController: UInt8 = 0
}
/// An object that manages drag-and-drop reordering of table view cells.
public var reorder: ReorderController {
if let controller = objc_getAssociatedObject(self, &AssociatedKeys.reorderController) as? ReorderController {
return controller
} else {
let controller = ReorderController(tableView: self)
objc_setAssociatedObject(self, &AssociatedKeys.reorderController, controller, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
return controller
}
}
}
然后委托看起来有点像这样:
public protocol TableViewReorderDelegate: class {
// A series of delegate methods like this are defined:
func tableView(_ tableView: UITableView, reorderRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath)
}
控制器看起来像这样:
public class ReorderController: NSObject {
/// The delegate of the reorder controller.
public weak var delegate: TableViewReorderDelegate?
// ... Other code here, can be found in the open source project
}
实现的关键是当快照单元格出现在触摸点时,有一个“间隔单元格”被插入到表格视图中,因此您需要在cellForRow:atIndexPath:
调用中处理间隔单元格:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let spacer = tableView.reorder.spacerCell(for: indexPath) {
return spacer
}
// otherwise build and return your regular cells
}
当然有办法。在您的手势识别器代码中调用方法 setEditing:animated:,这将使表格视图进入编辑模式。在苹果文档中查找“管理行的重新排序”以获取有关移动行的更多信息。