使用新 iOS 11 API 重新排序 tableview 时动画不正确
Incorrect animation when reordering tableview with new iOS 11 APIs
我在 iOS 11 上使用新的拖放 API 对同一应用程序内的表格视图中的单元格重新排序。
这是我对 UITableViewDragDelegate
和 UITableViewDropDelegate
的实现:
extension TasksViewController: UITableViewDragDelegate {
func tableView(_ tableView: UITableView, itemsForBeginning session: UIDragSession, at indexPath: IndexPath) -> [UIDragItem] {
let string = tasks[indexPath.row]
guard let data = string.data(using: .utf8) else { return [] }
let itemProvider = NSItemProvider(item: data as NSData, typeIdentifier: kUTTypePlainText as String)
let item = UIDragItem(itemProvider: itemProvider)
item.localObject = string
return [item]
}
}
extension TasksViewController: UITableViewDropDelegate {
func tableView(_ tableView: UITableView, canHandle session: UIDropSession) -> Bool {
return true
}
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) {
guard let destinationIndexPath = coordinator.destinationIndexPath,
let dragItem = coordinator.items.first?.dragItem,
let task = dragItem.localObject as? String,
let sourceIndexPath = coordinator.items.first?.sourceIndexPath else {
return
}
tableView.performBatchUpdates({
self.tasks.remove(at: sourceIndexPath.row)
self.tasks.insert(task, at: destinationIndexPath.row)
tableView.deleteRows(at: [sourceIndexPath], with: .none)
tableView.insertRows(at: [destinationIndexPath], with: .none)
})
coordinator.drop(dragItem, toRowAt: destinationIndexPath)
}
}
这工作正常,但有一个奇怪的故障。当最后一个单元格被拖动时,一旦它被放下,它会在表格视图的底部出现一瞬间然后消失。
我在这里错过了什么?
首先为swift4添加pressGesture这个例子
let longpress = UILongPressGestureRecognizer(target: self, action: #selector(longPressGestureRecognized(gestureRecognizer:)))
tableView.addGestureRecognizer(longpress)
功能:
@objc func longPressGestureRecognized(gestureRecognizer: UIGestureRecognizer) {
let longPress = gestureRecognizer as! UILongPressGestureRecognizer
let state = longPress.state
let locationInView = longPress.location(in: tableView)
let indexPath = tableView.indexPathForRow(at: locationInView)
struct My {
static var cellSnapshot : UIView? = nil
static var cellIsAnimating : Bool = false
static var cellNeedToShow : Bool = false
}
switch state {
case UIGestureRecognizerState.began:
if indexPath != nil {
self.Move = indexPath
var cell:UITableViewCell? = UITableViewCell()
cell = tableView.cellForRow(at: indexPath!)
My.cellSnapshot = snapshotOfCell(inputView: cell!)
var center = cell?.center
My.cellSnapshot!.center = center!
My.cellSnapshot!.alpha = 0.0
tableView.addSubview(My.cellSnapshot!)
UIView.animate(withDuration: 0.25, animations: { () -> Void in
center?.y = locationInView.y
My.cellIsAnimating = true
My.cellSnapshot!.center = center!
My.cellSnapshot!.transform = CGAffineTransform(scaleX: 1.05, y: 1.05)
My.cellSnapshot!.alpha = 0.98
cell?.alpha = 0.0
}, completion: { (finished) -> Void in
if finished {
My.cellIsAnimating = false
if My.cellNeedToShow {
My.cellNeedToShow = false
UIView.animate(withDuration: 0.25, animations: { () -> Void in
cell?.alpha = 1
})
} else {
cell?.isHidden = true
}
}
})
}
break;
case UIGestureRecognizerState.changed:
if My.cellSnapshot != nil {
var center = My.cellSnapshot!.center
center.y = locationInView.y
My.cellSnapshot!.center = center
if ((indexPath != nil) && (indexPath != Move)) && Move != nil {
self.list.insert(self.list.remove(at: Move!.row), at: indexPath!.row) //this line change row index
tableView.moveRow(at: Move!, to: indexPath!)
Move = indexPath
}
}
break;
default:
if Move != nil {
let cell = tableView.cellForRow(at: Move!)
if My.cellIsAnimating {
My.cellNeedToShow = true
} else {
cell?.isHidden = false
cell?.alpha = 0.0
}
UIView.animate(withDuration: 0.25, animations: { () -> Void in
My.cellSnapshot!.center = (cell?.center)!
My.cellSnapshot!.transform = CGAffineTransform.identity
My.cellSnapshot!.alpha = 0.0
cell?.alpha = 1.0
}, completion: { (finished) -> Void in
if finished {
self.Move = nil
My.cellSnapshot!.removeFromSuperview()
My.cellSnapshot = nil
}
})
}
break;
}
}
这会拍摄单元格视图快照
func snapshotOfCell(inputView: UIView) -> UIView {
UIGraphicsBeginImageContextWithOptions(inputView.bounds.size, false, 0.0)
inputView.layer.render(in: UIGraphicsGetCurrentContext()!)
let image = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
let cellSnapshot : UIView = 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
}
我希望它会起作用:)
只需要将table删除动画改为.automatic,像这样:
tableView.deleteRows(at: [sourceIndexPath], with: .automatic)
以后就不会出现那个奇怪的动画了
我在 iOS 11 上使用新的拖放 API 对同一应用程序内的表格视图中的单元格重新排序。
这是我对 UITableViewDragDelegate
和 UITableViewDropDelegate
的实现:
extension TasksViewController: UITableViewDragDelegate {
func tableView(_ tableView: UITableView, itemsForBeginning session: UIDragSession, at indexPath: IndexPath) -> [UIDragItem] {
let string = tasks[indexPath.row]
guard let data = string.data(using: .utf8) else { return [] }
let itemProvider = NSItemProvider(item: data as NSData, typeIdentifier: kUTTypePlainText as String)
let item = UIDragItem(itemProvider: itemProvider)
item.localObject = string
return [item]
}
}
extension TasksViewController: UITableViewDropDelegate {
func tableView(_ tableView: UITableView, canHandle session: UIDropSession) -> Bool {
return true
}
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) {
guard let destinationIndexPath = coordinator.destinationIndexPath,
let dragItem = coordinator.items.first?.dragItem,
let task = dragItem.localObject as? String,
let sourceIndexPath = coordinator.items.first?.sourceIndexPath else {
return
}
tableView.performBatchUpdates({
self.tasks.remove(at: sourceIndexPath.row)
self.tasks.insert(task, at: destinationIndexPath.row)
tableView.deleteRows(at: [sourceIndexPath], with: .none)
tableView.insertRows(at: [destinationIndexPath], with: .none)
})
coordinator.drop(dragItem, toRowAt: destinationIndexPath)
}
}
这工作正常,但有一个奇怪的故障。当最后一个单元格被拖动时,一旦它被放下,它会在表格视图的底部出现一瞬间然后消失。
我在这里错过了什么?
首先为swift4添加pressGesture这个例子
let longpress = UILongPressGestureRecognizer(target: self, action: #selector(longPressGestureRecognized(gestureRecognizer:)))
tableView.addGestureRecognizer(longpress)
功能:
@objc func longPressGestureRecognized(gestureRecognizer: UIGestureRecognizer) {
let longPress = gestureRecognizer as! UILongPressGestureRecognizer
let state = longPress.state
let locationInView = longPress.location(in: tableView)
let indexPath = tableView.indexPathForRow(at: locationInView)
struct My {
static var cellSnapshot : UIView? = nil
static var cellIsAnimating : Bool = false
static var cellNeedToShow : Bool = false
}
switch state {
case UIGestureRecognizerState.began:
if indexPath != nil {
self.Move = indexPath
var cell:UITableViewCell? = UITableViewCell()
cell = tableView.cellForRow(at: indexPath!)
My.cellSnapshot = snapshotOfCell(inputView: cell!)
var center = cell?.center
My.cellSnapshot!.center = center!
My.cellSnapshot!.alpha = 0.0
tableView.addSubview(My.cellSnapshot!)
UIView.animate(withDuration: 0.25, animations: { () -> Void in
center?.y = locationInView.y
My.cellIsAnimating = true
My.cellSnapshot!.center = center!
My.cellSnapshot!.transform = CGAffineTransform(scaleX: 1.05, y: 1.05)
My.cellSnapshot!.alpha = 0.98
cell?.alpha = 0.0
}, completion: { (finished) -> Void in
if finished {
My.cellIsAnimating = false
if My.cellNeedToShow {
My.cellNeedToShow = false
UIView.animate(withDuration: 0.25, animations: { () -> Void in
cell?.alpha = 1
})
} else {
cell?.isHidden = true
}
}
})
}
break;
case UIGestureRecognizerState.changed:
if My.cellSnapshot != nil {
var center = My.cellSnapshot!.center
center.y = locationInView.y
My.cellSnapshot!.center = center
if ((indexPath != nil) && (indexPath != Move)) && Move != nil {
self.list.insert(self.list.remove(at: Move!.row), at: indexPath!.row) //this line change row index
tableView.moveRow(at: Move!, to: indexPath!)
Move = indexPath
}
}
break;
default:
if Move != nil {
let cell = tableView.cellForRow(at: Move!)
if My.cellIsAnimating {
My.cellNeedToShow = true
} else {
cell?.isHidden = false
cell?.alpha = 0.0
}
UIView.animate(withDuration: 0.25, animations: { () -> Void in
My.cellSnapshot!.center = (cell?.center)!
My.cellSnapshot!.transform = CGAffineTransform.identity
My.cellSnapshot!.alpha = 0.0
cell?.alpha = 1.0
}, completion: { (finished) -> Void in
if finished {
self.Move = nil
My.cellSnapshot!.removeFromSuperview()
My.cellSnapshot = nil
}
})
}
break;
}
}
这会拍摄单元格视图快照
func snapshotOfCell(inputView: UIView) -> UIView {
UIGraphicsBeginImageContextWithOptions(inputView.bounds.size, false, 0.0)
inputView.layer.render(in: UIGraphicsGetCurrentContext()!)
let image = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
let cellSnapshot : UIView = 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
}
我希望它会起作用:)
只需要将table删除动画改为.automatic,像这样:
tableView.deleteRows(at: [sourceIndexPath], with: .automatic)
以后就不会出现那个奇怪的动画了