0

私は最初のコアデータアプリケーションを構築しており、ユーザーが持ち、ドラッグしてセルを移動できる機能を実装しようとしています。NSFetchedResultsControllerとコアデータを使用して移動するとUITableViewセルが消える

私が得ている問題は、細胞が解放されると消えることです。ただし、十分に下にスクロールしてポップアップすると、並べ替えられた順序ではなく元の順序で表示されます。

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

プライマリsortDescriptorであり、ユーザが終了するたびに変更される別のプロパティ(_sortOrder_)を作成する必要があります。あなたの例では、最初のToDoのsortOrderは、3番目のsortOrderより大きい値に割り当てられます。 – AgRizzo

+0

@ジェームズ:あなたは解決策を手に入れましたか? – Bucket

答えて

0

あなたのコードは、テーブルビューのUIを更新しますが、それはあなたのデータモデルを更新しません新しい注文をする次回のテーブルビューでセルを表示する必要がある場合、コードはドラッグ前と同じ情報を返します。たとえば、tableView(_:, cellForRowAt:)は何も起こらないため、古い順序でセルを返します。これは、テーブルビューがそのメソッドを呼び出す必要があるので、スクロールしてスクロールバックするまでは発生しません。あなたはそれを変更することはないので、古い注文は続行されます。一時的なUIの更新のみを行います。

テーブル内のアイテムを並べ替えることができるようにするには、そのオーダーを含むようにデータモデルを更新する必要があります。おそらく、それはsortOrderのような整数というCore Dataに新しいフィールドを追加することを意味します。次に、sortOrderをドラッグアンドドロップした新しい注文と一致するように更新します。

関連する問題