我从书中找到UITableView
与NSFetchedResultsController
一起使用。
后台流程会更新CoreData
个对象,因此每次更新都会自动触发FetchedResult
和UITableView
的更新。
在这些更新过程中向下拉UITableView
会导致一个令人不安的跳跃回到顶部,然后在进一步拉动时快速回到下拉位置:
实体:
@objc(Entity)
class Entity: NSManagedObject {
@NSManaged var name: String
@NSManaged var lastUpdated: NSDate
}
结果:
private lazy var resultsController: NSFetchedResultsController = {
let fetchRequest = NSFetchRequest( entityName: "Entity" )
fetchRequest.sortDescriptors = [ NSSortDescriptor( key: "name", ascending: true ) ]
fetchRequest.relationshipKeyPathsForPrefetching = [ "Entity" ]
let controller = NSFetchedResultsController(
fetchRequest: fetchRequest,
managedObjectContext: self.mainContext,
sectionNameKeyPath: nil,
cacheName: nil
)
controller.delegate = self
controller.performFetch( nil )
return controller
}()
组合UITableView
和ResultsController
的通常更新逻辑:
func controllerWillChangeContent(controller: NSFetchedResultsController) {
tableView.beginUpdates()
}
func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) {
switch( type ) {
case NSFetchedResultsChangeType.Insert:
tableView.insertRowsAtIndexPaths( [ newIndexPath! ], withRowAnimation: UITableViewRowAnimation.Fade )
case NSFetchedResultsChangeType.Delete:
tableView.deleteRowsAtIndexPaths( [ indexPath! ], withRowAnimation: UITableViewRowAnimation.Fade )
case NSFetchedResultsChangeType.Update:
tableView.reloadRowsAtIndexPaths( [ indexPath! ], withRowAnimation: UITableViewRowAnimation.None )
case NSFetchedResultsChangeType.Move:
tableView.moveRowAtIndexPath( indexPath!, toIndexPath: newIndexPath! )
default:
break
}
}
显示效果的背景更新(不是原始的更复杂的):
override func viewDidLoad() {
super.viewDidLoad()
//...
let timer = NSTimer.scheduledTimerWithTimeInterval( 1.0, target: self, selector: "refresh", userInfo: nil, repeats: true)
NSRunLoop.mainRunLoop().addTimer( timer, forMode: NSRunLoopCommonModes )
}
func refresh() {
let request = NSFetchRequest( entityName: "Entity" )
if let result = mainContext.executeFetchRequest( request, error: nil ) {
for entity in result as [Entity] {
entity.lastUpdated = NSDate()
}
}
}
我已经搜索了stackoverflow几个小时,并尝试了几乎所有的东西。
唯一的想法就是停止tableView.reloadRowsAtIndexPaths()
而不是beginUpdates()
/ endUpdates()
,所以两者似乎都会造成这种影响。
但这不是选项,因为UITableView
根本不会更新。
任何建议或解决方案?
答案 0 :(得分:1)
你能做的很简单; 不要重新加载表格以进行coredata更新,首先检查表格是否静止不动..
当您需要从coredata更新更新tableview时,请检查tableview是否正在滚动(有很多方法可以执行此操作)。如果是这样,不要继续,但要这样做:在tableview类标记中,表之后需要使用布尔值更新表,例如“shouldUpdateTable”。 比用于“完成滚动动画”的tableview委托方法检查表是否需要从该布尔标记重新加载,如果为true,则重新加载表并重置布尔值。
希望这种方法有所帮助。
答案 1 :(得分:1)
我刚刚找到了一个适合我的解决方案。
有两项操作与我的后台更新有冲突:
因此,根据Timur X的建议,这两项行动都必须锁定更新。这就是我所做的:
出于多种原因的锁定机制
enum LockReason: Int {
case scrolling = 0x01,
editing = 0x02
}
var locks: Int = 0
func setLock( reason: LockReason ) {
locks |= reason.rawValue
resultsController.delegate = nil
NSLog( "lock set = \(locks)" )
}
func removeLock( reason: LockReason ) {
locks &= ~reason.rawValue
NSLog( "lock removed = \(locks)" )
if 0 == locks {
resultsController.delegate = self
}
}
集成滚动锁定机制
override func scrollViewWillBeginDragging(scrollView: UIScrollView) {
setLock( .scrolling )
}
override func scrollViewDidEndDragging(scrollView: UIScrollView, willDecelerate decelerate: Bool) {
if !decelerate {
removeLock( .scrolling )
}
}
override func scrollViewDidEndDecelerating(scrollView: UIScrollView) {
removeLock( .scrolling )
}
处理编辑(在这种情况下删除)
override func tableView(tableView: UITableView, willBeginEditingRowAtIndexPath indexPath: NSIndexPath) {
setLock( .editing )
}
override func tableView(tableView: UITableView, didEndEditingRowAtIndexPath indexPath: NSIndexPath) {
removeLock( .editing )
}
,这是编辑/删除
的整合override func tableView(tableView: UITableView, titleForDeleteConfirmationButtonForRowAtIndexPath indexPath: NSIndexPath) -> String! {
return "delete"
}
override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
return true
}
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == UITableViewCellEditingStyle.Delete {
removeLock( .editing )
if let entity = resultsController.objectAtIndexPath( indexPath ) as? Entity {
mainContext.deleteObject( entity )
}
}
}