gpt4 book ai didi

ios - 使用 NSFetchedResultsController 和 Core Data 移动时 UITableView 单元格消失

转载 作者:行者123 更新时间:2023-11-30 12:00:50 24 4
gpt4 key购买 nike

我正在构建我的第一个 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()
}
}

最佳答案

您的代码会更新 TableView 的 UI,但不会更新您的数据模型以获取新顺序。下次表格 View 需要显示单元格时,您的代码会为其提供与拖动之前相同的信息 - 例如,tableView(_:, cellForRowAt:) 仍按旧顺序返回单元格,因为没有什么可以改变那里发生的事情。直到您滚动然后再向后滚动时,这种情况才会发生,因为那时 TableView 需要调用该方法。旧的顺序将继续,只是因为您从未更改过它,您只是进行了临时的 UI 更新。

如果您希望能够对表中的项目进行重新排序,则需要更新数据模型以包含该顺序。可能这意味着在 Core Data 中添加一个新字段,一个名为 sortOrder 之类的整数。然后更新 sortOrder 以便它与拖放中的新顺序匹配。

关于ios - 使用 NSFetchedResultsController 和 Core Data 移动时 UITableView 单元格消失,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47261174/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com