【问题标题】:NSFetchedResultsController like iOS Messages (recent items first)NSFetchedResultsController like iOS Messages(最近的项目优先)
【发布时间】:2016-02-27 21:35:17
【问题描述】:

我想按照 iOS Messages 的工作方式设置 NSFetchedResultsController,这意味着,我想先获取最新的项目以填满屏幕,然后在用户在 tableview 中回滚历史时获取。

我认为我对仅使用 FetchedResultsController 有一点偏见,它是“正常”的代表,我不太确定如何去做。

我也不确定这是否是我想要获得的正确工具:)

我只想获取最近的记录,将它们显示在表格视图中,当用户向上滚动时,继续获取项目并将它们插入现有行的上方。

这只是我目前的常规设置:

import UIKit
import CoreData

class ViewController: UIViewController {

var coreDataStack: CoreDataStack!

@IBOutlet weak var tableView: UITableView!
@IBOutlet weak var addButton: UIBarButtonItem!

var fetchedResultsController: NSFetchedResultsController!

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.


    let fetchRequest    = NSFetchRequest(entityName: "Item")
    let timestampSort   = NSSortDescriptor(key: "timestamp", ascending: true)
    fetchRequest.sortDescriptors = [timestampSort]

    fetchedResultsController =
        NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: coreDataStack.context, sectionNameKeyPath: nil, cacheName: nil)

    self.fetchedResultsController.delegate = self

    do {

        try self.fetchedResultsController.performFetch()
    } catch let error as NSError {
        print("initial fetch error is: \(error.localizedDescription)")
    }
}

}

extension ViewController: UITableViewDataSource {

func numberOfSectionsInTableView
    (tableView: UITableView) -> Int {

        return self.fetchedResultsController.sections!.count
}

func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
    let sectionInfo = fetchedResultsController.sections![section]
    return sectionInfo.name
}

func tableView(tableView: UITableView,
    numberOfRowsInSection section: Int) -> Int {
        let sectionInfo = self.fetchedResultsController.sections![section]
        return sectionInfo.numberOfObjects
}

func tableView(tableView: UITableView,
    cellForRowAtIndexPath indexPath: NSIndexPath)
    -> UITableViewCell {

        let cell =
        tableView.dequeueReusableCellWithIdentifier(
            "ItemCell", forIndexPath: indexPath)
            as! ItemCell

        let item = self.fetchedResultsController.objectAtIndexPath(indexPath) as! Item

        cell.textLabel.text = item.name

        return cell
}
}

extension ViewController: NSFetchedResultsControllerDelegate {

func controllerWillChangeContent(controller: NSFetchedResultsController) {
    self.tableView.beginUpdates()
}

func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) {

    switch type {

    case .Insert:
        self.tableView.insertRowsAtIndexPaths([newIndexPath!], withRowAnimation: .Automatic)

    case .Delete:
        self.tableView.deleteRowsAtIndexPaths([indexPath!], withRowAnimation: .Automatic)

    case .Update:
        return

    case .Move:
        self.tableView.deleteRowsAtIndexPaths([indexPath!], withRowAnimation: .Automatic)
        self.tableView.insertRowsAtIndexPaths([newIndexPath!], withRowAnimation: .Automatic)

    }
}

func controllerDidChangeContent(controller: NSFetchedResultsController) {
    self.tableView.endUpdates()
}

}

【问题讨论】:

  • 我希望它按升序排序,但我只想获取最后 x 条记录,然后在用户向上滚动时获取较早的记录。如果有 100 条记录,我想要最后 20 条,按升序排序,然后用户可以向上滚动,它将获取前 80 条

标签: ios swift core-data nsfetchedresultscontroller


【解决方案1】:

NSFetchedResultsController 只是按排序顺序存储对象数组,您可以通过fetchedObjects 方法访问这些对象。因此,要显示最后 X 条消息,您需要显示该数组的最后 X 个元素。

与其尝试在每个numberOfRowsInSection()cellForRowAtIndexPath() 中计算它,我发现每次NSFetchedResultsController 更改时缓存您当前显示的X 元素的副本更容易(在controllerDidChangeContent() )。也就是说,在每次调用 controllerDidChangeContent 时,您都会从 fetched results controller 的 fetchedObjects 复制最后 X 个元素

(Objective-C 中的示例代码,因为这是我在项目中使用的代码)

@property (strong, nonatomic) NSArray *msgsToDisplay;
@property unsigned long numToDisplay;
@property unsigned long numberActuallyDisplaying;


- (void)viewDidLoad {
    // ...
    self.msgsToDisplay = [[NSArray alloc] init]; 
    self.numToDisplay = 20; // or whatever count you want to display initially
    // ...
}

- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
    NSArray *allMsgs = [[_fetchedResultsController fetchedObjects] copy];
    self.numberActuallyDisplaying = MIN(self.numToDisplay, [allMsgs count]);

    self.msgsToDisplay = [allMsgs subarrayWithRange:NSMakeRange([allMsgs count] - self.numberActuallyDisplaying, self.numberActuallyDisplaying)];
}

那么您的行数(假设表中只有一个部分)就是您实际显示的消息数:

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return self.numberActuallyDisplaying;
}

cellForRowAtIndexPath 可以索引到您缓存的对象副本:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    Message *msg = [self.msgsToDisplay objectAtIndex:indexPath.row];
    //...
}

当用户向上滚动时,您可以使用UIRefreshControl (https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIRefreshControl_class/) 来允许用户请求更多数据。看起来您没有使用UITableViewController,因此您需要显式创建UIRefreshControl 并将其添加到表中。在viewDidLoad():

UIRefreshControl *refreshControl = [[UIRefreshControl alloc] init];
[refreshControl addTarget:self action:@selector(handleRefresh:) forControlEvents:UIControlEventValueChanged];
[self.tableView insertSubview:refreshControl atIndex:0];

当用户下拉刷新时,您可以将您的self.numToDisplay设置为更大的数字,然后根据新的数字更新您的self.msgsToDisplayself.numActuallyDisplaying以显示。

- (void) handleRefresh:(UIRefreshControl *)controller
{
    self.numToDisplay += NUMBER_TO_DISPLAY_INCREMENT;

    __block NSArray *allMsgs;

    [[_fetchedResultsController managedObjectContext] performBlockAndWait:^{
        allMsgs = [[_fetchedResultsController fetchedObjects] copy];
    }];

    self.numberActuallyDisplaying = MIN(self.numToDisplay, [allMsgs count]);
    self.msgsToDisplay = [allMsgs subarrayWithRange:NSMakeRange([allMsgs count] - self.numberActuallyDisplaying, self.numberActuallyDisplaying)];

    [controller endRefreshing];
}

将这一切转换为 Swift 应该很简单,但如果您需要帮助,请告诉我。

【讨论】:

  • 很好的答案,我明白了这个概念并认为它会很好地工作,谢谢。
猜你喜欢
  • 2019-10-14
  • 1970-01-01
  • 2018-08-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-02-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多