我正在构建我的第一个Core Data应用程序,并尝试实现一个功能,用户可以通过按住并拖动来移动单元格。
我得到的问题是当细胞释放时它们会消失。但是,如果向下滚动然后弹回,它们会重新出现,虽然按照原始顺序,而不是重新排列的顺序。
A screen-recording of the bug can be found here
知道我哪里出错了?
我的UITableViewController子类:
import UIKit
import CoreData
import CoreGraphics
class MainTableViewController: UITableViewController {
let context = AppDelegate.viewContext
lazy var fetchedResultsController: TodoFetchedResultsController = {
return TodoFetchedResultsController(managedObjectContext: self.context, tableView: self.tableView)
}()
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.leftBarButtonItem = editButtonItem
fetchedResultsController.tryFetch()
let longPressRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(longPressGestureRecognized(gestureRecognizer:)))
tableView.addGestureRecognizer(longPressRecognizer)
}
var snapshot: UIView? = nil
var path: IndexPath? = nil
@objc
func longPressGestureRecognized(gestureRecognizer: UILongPressGestureRecognizer) {
let state = gestureRecognizer.state
let locationInView = gestureRecognizer.location(in: tableView)
let indexPath = tableView.indexPathForRow(at: locationInView)
switch state {
case .began:
if indexPath != nil {
self.path = indexPath
let cell = tableView.cellForRow(at: indexPath!) as! TodoTableViewCell
snapshot = snapshotOfCell(cell)
var center = cell.center
snapshot!.center = center
snapshot!.alpha = 0.0
tableView.addSubview(snapshot!)
UIView.animate(withDuration: 0.1, animations: { () -> Void in
center.y = locationInView.y
self.snapshot!.center = center
self.snapshot!.transform = CGAffineTransform(scaleX: 1.05, y: 1.05)
self.snapshot!.alpha = 0.98
cell.alpha = 0.0
}, completion: { (finished) -> Void in
if finished {
cell.isHidden = true
}
})
}
case .changed:
if self.snapshot != nil {
var center = snapshot!.center
center.y = locationInView.y
snapshot!.center = center
if ((indexPath != nil) && (indexPath != self.path)) {
// Move cells
tableView.moveRow(at: self.path!, to: indexPath!)
self.path = indexPath
}
}
default:
if self.path != nil {
let cell = tableView.cellForRow(at: self.path!) as! TodoTableViewCell
cell.isHidden = false
cell.alpha = 0.0
UIView.animate(withDuration: 0.1, animations: { () -> Void in
self.snapshot!.center = cell.center
self.snapshot!.transform = CGAffineTransform.identity
self.snapshot!.alpha = 0.0
cell.alpha = 1.0
}, completion: { (finished) -> Void in
if finished {
cell.isHidden = true // FIXME: - Something up here?
}
})
}
}
}
func snapshotOfCell(_ inputView: UIView) -> UIView {
UIGraphicsBeginImageContextWithOptions(inputView.bounds.size, false, 0.0)
guard let graphicsContext = UIGraphicsGetCurrentContext() else { fatalError() }
inputView.layer.render(in: graphicsContext)
et 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
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return fetchedResultsController.sections?.count ?? 0
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
guard let section = fetchedResultsController.sections?[section] else { return 0 }
return section.numberOfObjects
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Todo Cell", for: indexPath) as! TodoTableViewCell
return configureCell(cell, at: indexPath)
}
private func configureCell(_ cell: TodoTableViewCell, at indexPath: IndexPath) -> TodoTableViewCell {
let todo = fetchedResultsController.object(at: indexPath)
do {
try cell.update(with: todo)
} catch {
print("\(error)")
}
return cell
}
func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
return true
}
// Support editing the table view.
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
let todoToDelete = fetchedResultsController.object(at: indexPath)
context.delete(todoToDelete)
} else if editingStyle == .insert {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
}
(UIApplication.shared.delegate as! AppDelegate).saveContext()
tableView.reloadData()
}
// MARK: - Navigation
...
}
我的NSFetchedResultsControllerSubclass:
class TodoFetchedResultsController: NSFetchedResultsController<Todo>, NSFetchedResultsControllerDelegate {
private let tableView: UITableView
init(managedObjectContext: NSManagedObjectContext, tableView: UITableView) {
self.tableView = tableView
let request: NSFetchRequest<Todo> = Todo.fetchRequest()
let sortDescriptor = NSSortDescriptor(key: "dueDate", ascending: true)
request.sortDescriptors = [sortDescriptor]
super.init(fetchRequest: request, managedObjectContext: managedObjectContext, sectionNameKeyPath: nil, cacheName: nil)
self.delegate = self
tryFetch()
}
func tryFetch() {
do {
try performFetch()
} catch {
print("Unresolved error: \(error)")
}
}
// MARK: - Fetched Results Controlle Delegate
// Handle insertion, deletion, moving and updating of rows.
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>,
didChange anObject: Any,
at indexPath: IndexPath?,
for type: NSFetchedResultsChangeType,
newIndexPath: IndexPath?) {
switch type {
case .insert:
if let insertIndexPath = newIndexPath {
self.tableView.insertRows(at: [insertIndexPath], with: .fade)
}
case .delete:
if let deleteIndexPath = indexPath {
self.tableView.deleteRows(at: [deleteIndexPath], with: .fade)
}
case .update:
if let updateIndexPath = indexPath {
if let cell = self.tableView.cellForRow(at: updateIndexPath) as! TodoTableViewCell? {
let todo = self.object(at: updateIndexPath)
do {
try cell.update(with: todo)
} catch {
print("error updating cell: \(error)")
}
}
}
case .move:
if let deleteIndexPath = indexPath {
self.tableView.deleteRows(at: [deleteIndexPath], with: .fade)
}
if let insertIndexPath = newIndexPath {
self.tableView.insertRows(at: [insertIndexPath], with: .fade)
}
break
}
}
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
tableView.reloadData()
}
}
答案 0 :(得分:0)
您的代码更新了表格视图的用户界面,但它不会更新您的数据模型以获得新订单。下次表格视图需要显示单元格时,代码会为其提供与拖动前相同的信息 - 例如,sortOrder
仍会按旧顺序返回单元格,因为没有任何更改那里发生了什么。直到您滚动然后向后滚动才会发生这种情况,因为当表视图需要调用该方法时。旧订单将继续,只是因为您永远不会更改它,您只进行临时UI更新。
如果您希望能够重新订购表格中的项目,则需要更新数据模型以包含该订单。可能这意味着在Core Data中添加一个新字段,这个整数称为sortOrder
。然后更新embedding = tf.get_variable("embedding", [1000000000, 20],
partitioner=tf.fixed_size_partitioner(3))
,使其与拖放中的新订单匹配。