重新排序单元格后如何更改 fetchresult 控制器数组中对象的顺序
How to change the order of objects in the array of fetchresultcontroller after reordering the cells
我有一个 table 视图,我刚刚实现了一个 class 来帮助我重新排序单元格,就像 table 视图委托附带的常规移动单元格方法。
现在,在对单元格重新排序后,我需要将保存单元格对象的数组更改为新顺序...我该怎么做?
这是我重新排序单元格的方法:
- (void)moveTableView:(FMMoveTableView *)tableView moveRowFromIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath { NSArray
}
我有一个 coreDataStack class 负责处理所有核心数据(创建单例),它看起来像这样:
#import "CoreDataStack.h"
@implementation CoreDataStack
#pragma mark - Core Data stack
@synthesize managedObjectContext = _managedObjectContext;
@synthesize managedObjectModel = _managedObjectModel;
@synthesize persistentStoreCoordinator = _persistentStoreCoordinator;
+ (instancetype)defaultStack {
static CoreDataStack *defaultStack;
static dispatch_once_t onceTocken;
dispatch_once (&onceTocken, ^{
defaultStack = [[self alloc] init];
});
return defaultStack;
}
- (NSURL *)applicationDocumentsDirectory {
// The directory the application uses to store the Core Data store file. This code uses a directory named "digitalCrown.Lister" in the application's documents directory.
return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
}
- (NSManagedObjectModel *)managedObjectModel {
// The managed object model for the application. It is a fatal error for the application not to be able to find and load its model.
if (_managedObjectModel != nil) {
return _managedObjectModel;
}
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"Lister" withExtension:@"momd"];
_managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
return _managedObjectModel;
}
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
// The persistent store coordinator for the application. This implementation creates and return a coordinator, having added the store for the application to it.
if (_persistentStoreCoordinator != nil) {
return _persistentStoreCoordinator;
}
// Create the coordinator and store
_persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"Lister.sqlite"];
NSError *error = nil;
NSString *failureReason = @"There was an error creating or loading the application's saved data.";
if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) {
// Report any error we got.
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
dict[NSLocalizedDescriptionKey] = @"Failed to initialize the application's saved data";
dict[NSLocalizedFailureReasonErrorKey] = failureReason;
dict[NSUnderlyingErrorKey] = error;
error = [NSError errorWithDomain:@"YOUR_ERROR_DOMAIN" code:9999 userInfo:dict];
// Replace this with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
return _persistentStoreCoordinator;
}
- (NSManagedObjectContext *)managedObjectContext {
// Returns the managed object context for the application (which is already bound to the persistent store coordinator for the application.)
if (_managedObjectContext != nil) {
return _managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (!coordinator) {
return nil;
}
_managedObjectContext = [[NSManagedObjectContext alloc] init];
[_managedObjectContext setPersistentStoreCoordinator:coordinator];
return _managedObjectContext;
}
#pragma mark - Core Data Saving support
- (void)saveContext {
NSManagedObjectContext *managedObjectContext = self.managedObjectContext;
if (managedObjectContext != nil) {
NSError *error = nil;
if ([managedObjectContext hasChanges] && ![managedObjectContext save:&error]) {
// Replace this implementation with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
}
}
@end
每当我向核心数据添加新对象时,我都会这样做:
- (void)insertTeget {
CoreDataStack *stack = [CoreDataStack defaultStack];
Target *target = [NSEntityDescription insertNewObjectForEntityForName:@"Target" inManagedObjectContext:stack.managedObjectContext];
if (self.myTextView.text != nil) {
target.body = self.myTextView.text;
target.time = [NSDate date];
}
[stack saveContext];
}
在 table 视图中,当我获取数据时,我是这样做的:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellIdentifier = @"StackTableViewCell";
Target *target = [self.fetchedResultController objectAtIndexPath:indexPath];
StackTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (!cell)
{
NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:@"StackTableViewCell" owner:self options:nil];
cell = [topLevelObjects objectAtIndex:0];
}
cell.cellLabel.text = target.body;
cell.cellLabel.font = [UIFont fontWithName:@"Candara-Bold" size:20];
cell.showsReorderControl = YES;
// Configure the cell...
return cell;
}
这是我在 table 视图控制器中的 fetchresultconroller/fetch 请求配置 class:
- (NSFetchRequest *)targetsFetchRequest {
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Target"];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"time" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
return fetchRequest;
}
- (NSFetchedResultsController *)fetchedResultController {
if (_fetchedResultController != nil) {
return _fetchedResultController;
}
CoreDataStack *stack = [CoreDataStack defaultStack];
NSFetchRequest *fetchRequest = [self targetsFetchRequest];
_fetchedResultController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:stack.managedObjectContext sectionNameKeyPath:nil cacheName:nil];
_fetchedResultController.delegate = self;
return _fetchedResultController;
}
我想要完成的是,每当用户创建一个目标对象时,它都会到达数组的末尾(所以它就像一个队列),如果用户移动单元格,那么我需要更改数据库数组的顺序...
移动单元格方法:
- (void)moveTableView:(FMMoveTableView *)tableView moveRowFromIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath {
int start = 0;
int end = 0;
if (fromIndexPath.row > toIndexPath.row) {
start = (int)fromIndexPath.row;
end = (int)toIndexPath.row;
} else {
start = (int)toIndexPath.row;
end = (int)fromIndexPath.row;
}
for (int i = start; i <= end; ++i) {
Target *target = [self.fetchedResultController objectAtIndexPath:[NSIndexPath indexPathForRow:i inSection:0]];
[target setOrder:@(i)];
}
[[CoreDataStack defaultStack] saveContext];
// a test to see if the order is changed
[self.fetchedResultController performFetch:nil];
NSArray *arr = [self.fetchedResultController fetchedObjects];
for (int i=0; i<arr.count; i++) {
Target *ta = [arr objectAtIndex:i];
NSLog(@"%@",ta.body);
}
}
日志:
2015-04-14 10:29:13.405 Lister[3163:477453] One
2015-04-14 10:29:13.406 Lister[3163:477453] Two
2015-04-14 10:29:13.406 Lister[3163:477453] Three
2015-04-14 10:29:13.407 Lister[3163:477453] Four
2015-04-14 10:29:13.407 Lister[3163:477453] Five
2015-04-14 10:29:21.070 Lister[3163:477453]
2015-04-14 10:29:21.071 Lister[3163:477453] One
2015-04-14 10:29:21.071 Lister[3163:477453] Two
2015-04-14 10:29:21.071 Lister[3163:477453] Three
2015-04-14 10:29:21.072 Lister[3163:477453] Four
2015-04-14 10:29:21.072 Lister[3163:477453] Five
2015-04-14 10:29:25.037 Lister[3163:477453]
2015-04-14 10:29:25.039 Lister[3163:477453] One
2015-04-14 10:29:25.039 Lister[3163:477453] Two
2015-04-14 10:29:25.040 Lister[3163:477453] Three
2015-04-14 10:29:25.040 Lister[3163:477453] Four
2015-04-14 10:29:25.041 Lister[3163:477453] Five
此外,单元格的标签现在表现得很奇怪,如果将带有标签 "one" 的单元格移动到带有标签 "two" 的单元格的索引处,那么 [=39] 的标签=] 正在更改为 "two"。所以我遇到了 2 个单元格具有相同标签的情况。
如果您希望 NSFetchResultsController 接受更改,则必须更改基础数据模型以反映新订单。
嗯,最简单的解决方案是
- 向您的
Target
实体添加一个属性,例如 order
类型 Integer32
。
创建和插入新对象
每当您创建一个新的 Target
对象时,首先使用具有键 @"order"
和 ascending=YES
的 sortDescriptor
从数据库中获取现有对象。获取此获取数组的最后一个对象并检查其顺序。现在在您的新 Target
对象中增加订单并将其插入数据库。如果取出数组returns0个对象,则设置order=@(0)
.
- (void)insertTeget {
CoreDataStack *stack = [CoreDataStack defaultStack];
//Fetching objects from database
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Target"];
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"order" ascending:YES];
[fetchRequest setSortDescriptors:@[sortDescriptor]];
NSArray *existingObjects = [stack.managedObjectContext executeFetchRequest:fetchRequest error:nil];
//Creating new object
Target *target = [NSEntityDescription insertNewObjectForEntityForName:@"Target" inManagedObjectContext:stack.managedObjectContext];
if (self.myTextView.text != nil) {
target.body = self.myTextView.text;
target.order = @([(Target *)existingObjects.lastObject order].integerValue + 1);
}
[stack saveContext];
}
NSFetchedResultsController
- 使用上面定义的
sortDescriptor
获取对象。
Taken from your code
- (NSFetchRequest *)targetsFetchRequest {
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Target"];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"order" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
return fetchRequest;
}
- (NSFetchedResultsController *)fetchedResultController {
if (_fetchedResultController != nil) {
return _fetchedResultController;
}
CoreDataStack *stack = [CoreDataStack defaultStack];
NSFetchRequest *fetchRequest = [self targetsFetchRequest];
_fetchedResultController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:stack.managedObjectContext sectionNameKeyPath:nil cacheName:nil];
_fetchedResultController.delegate = self;
return _fetchedResultController;
}
重新排列单元格
现在,当您在 table 视图中重新排列单元格时,您只需要 运行 一个 for 循环并更新它们的顺序。您只需要更新两个索引路径之间的 order
个对象。
- (void)moveTableView:(FMMoveTableView *)tableView moveRowFromIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath {
int start = 0;
int end = 0;
if (fromIndexPath.row > toIndexPath.row) {
start = fromIndexPath.row;
end = toIndexPath.row;
} else {
start = toIndexPath.row;
end = fromIndexPath.row;
}
for (int i = start; i <= end; ++i) {
Target *target = [self.fetchedResultsController objectAtIndexPath:[NSIndexPath indexPathForRow:i inSection:0]];
[target setOrder:@(i)];
}
[[CoreDataStack defaultStack] saveContext];
}
注意:上述解决方案假设您有order
从0开始。
当您创建和插入新的 Target
对象时,您需要实施 NSFetchedResultsController
委托方法来为这些对象添加相应的行。由于我们已经定义了 sortDescriptor
,新行将添加到 tableView
.
的末尾
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
[self.tableView beginUpdates];
}
- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo
atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type {
switch(type) {
case NSFetchedResultsChangeInsert:
[self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex]
withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject
atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type
newIndexPath:(NSIndexPath *)newIndexPath {
UITableView *tableView = self.tableView;
switch(type) {
case NSFetchedResultsChangeInsert:
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]
withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeUpdate:
break;
case NSFetchedResultsChangeMove:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
withRowAnimation:UITableViewRowAnimationFade];
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]
withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
[self.tableView endUpdates];
}
简单的解决方案: Reffer Apple's Doc:
创建一个 NSMutableArray 来识别重新排序的数组。
第 1 步: 在 header 或 class 文件中声明一个 NSMutableArray
属性 @property (strong, nonatomic) NSMutableArray *arrayTag
。
步骤 2: 在 viewDidLoad
中初始化
步骤 3: 在 tableview
委托方法中添加此代码
-(void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath
{
NSString *stringToMove = [arrayTag objectAtIndex:sourceIndexPath.row];
[arrayTag removeObjectAtIndex:sourceIndexPath.row];
[arrayTag insertObject:stringToMove atIndex:destinationIndexPath.row];
}
试试这个
-(void)moveTableView:(UITableView *)tableView moveRowFromIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath {
NSString *str1 = [[yourArray objectAtIndex:fromIndexPath.row] copy];
NSString *str2 = [[yourArray objectAtIndex:toIndexPath.row] copy];
[yourArray replaceObjectAtIndex:fromIndexPath.row withObject:str2];
[yourArray replaceObjectAtIndex:toIndexPath.row withObject:str1];
[tableView reloadData];
}
如果我正确理解了你的问题,你必须更改模型的架构。
一个。一、我的理解
您有一个项目列表。最后添加新项目。现在您想让用户能够以自定义方式重新订购商品。
乙。你做什么
实际上您正在使用创建日期属性进行订购。当然你不能用它来重新排序,因为这意味着改变创建日期。因此,使用创建日期的整个方法都失败了:对于用户未更改的列表来说已经足够了,但如果您有自定义订单,则不行。
摄氏度。你能做什么
如果您有自定义订单,则需要自定义属性来反映订单。如果列表是实例对象的 属性,您可以使用 NSOrderedSet
和 Core Data 的有序关系来这样做。我不会因为回报。但是,如果适合您,您可以这样做。
否则你必须自己处理:
一个。将属性 order
添加到您的实体类型。
b。当你插入一个新对象时,检查现有列表的计数(取决于你如何持有它)并将值设置为新实例的order
属性。
c。抓取的时候,用那个属性排序。
d。更改时,更改实例对象的属性以及源和目标之间的所有实例对象的属性。让我解释一下:
我们有这样的 lis:
name order
Amin 0
Negm 1
Awad 2
Answer 3
现在,例如,Answer 被向上 从位置 3 移动到位置 1(在 Negm 之前):
name order
Amin 0
Answer 3
Negm 1
Awad 2
这意味着必须将移动对象 (3) 的顺序属性更改为新目标 (1),并且必须将顺序属性为 >=1 到 <3 的所有对象的顺序属性更改为 + 1. (显然,移动对象的顺序属性也是如此)
name order
Amin 0
Answer 1
Negm 2
Awad 3
在代码中
NSUInteger oldIndex = …; // 3
NSUInteger newIndex = …; // 1
movedObject.order = newIndex;
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Target"];
NSPredicate *betweenPredicate = [NSPredicate predicateWithFormat:@"order >= %ld AND order < %ld", newIndex, oldIndex];
NSArray *objectsToChange = [context executeFetchRequest:request error:NULL];
for( Target *target in objectsToChange )
{
target.order = @([target.order unsignedIntegerValue] + 1);
}
如果一个项目被向下移动,你必须反过来做同样的事情。
如果您有不同的唯一对象列表,例如 iTunes 中的播放列表,您需要一个额外的实体类型而不是一个额外的属性。让我知道,我将 post 我的一本书中的代码,包括移动一个空白的项目列表。
我有一个 table 视图,我刚刚实现了一个 class 来帮助我重新排序单元格,就像 table 视图委托附带的常规移动单元格方法。
现在,在对单元格重新排序后,我需要将保存单元格对象的数组更改为新顺序...我该怎么做?
这是我重新排序单元格的方法:
- (void)moveTableView:(FMMoveTableView *)tableView moveRowFromIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath { NSArray
}
我有一个 coreDataStack class 负责处理所有核心数据(创建单例),它看起来像这样:
#import "CoreDataStack.h"
@implementation CoreDataStack
#pragma mark - Core Data stack
@synthesize managedObjectContext = _managedObjectContext;
@synthesize managedObjectModel = _managedObjectModel;
@synthesize persistentStoreCoordinator = _persistentStoreCoordinator;
+ (instancetype)defaultStack {
static CoreDataStack *defaultStack;
static dispatch_once_t onceTocken;
dispatch_once (&onceTocken, ^{
defaultStack = [[self alloc] init];
});
return defaultStack;
}
- (NSURL *)applicationDocumentsDirectory {
// The directory the application uses to store the Core Data store file. This code uses a directory named "digitalCrown.Lister" in the application's documents directory.
return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
}
- (NSManagedObjectModel *)managedObjectModel {
// The managed object model for the application. It is a fatal error for the application not to be able to find and load its model.
if (_managedObjectModel != nil) {
return _managedObjectModel;
}
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"Lister" withExtension:@"momd"];
_managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
return _managedObjectModel;
}
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
// The persistent store coordinator for the application. This implementation creates and return a coordinator, having added the store for the application to it.
if (_persistentStoreCoordinator != nil) {
return _persistentStoreCoordinator;
}
// Create the coordinator and store
_persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"Lister.sqlite"];
NSError *error = nil;
NSString *failureReason = @"There was an error creating or loading the application's saved data.";
if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) {
// Report any error we got.
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
dict[NSLocalizedDescriptionKey] = @"Failed to initialize the application's saved data";
dict[NSLocalizedFailureReasonErrorKey] = failureReason;
dict[NSUnderlyingErrorKey] = error;
error = [NSError errorWithDomain:@"YOUR_ERROR_DOMAIN" code:9999 userInfo:dict];
// Replace this with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
return _persistentStoreCoordinator;
}
- (NSManagedObjectContext *)managedObjectContext {
// Returns the managed object context for the application (which is already bound to the persistent store coordinator for the application.)
if (_managedObjectContext != nil) {
return _managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (!coordinator) {
return nil;
}
_managedObjectContext = [[NSManagedObjectContext alloc] init];
[_managedObjectContext setPersistentStoreCoordinator:coordinator];
return _managedObjectContext;
}
#pragma mark - Core Data Saving support
- (void)saveContext {
NSManagedObjectContext *managedObjectContext = self.managedObjectContext;
if (managedObjectContext != nil) {
NSError *error = nil;
if ([managedObjectContext hasChanges] && ![managedObjectContext save:&error]) {
// Replace this implementation with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
}
}
@end
每当我向核心数据添加新对象时,我都会这样做:
- (void)insertTeget {
CoreDataStack *stack = [CoreDataStack defaultStack];
Target *target = [NSEntityDescription insertNewObjectForEntityForName:@"Target" inManagedObjectContext:stack.managedObjectContext];
if (self.myTextView.text != nil) {
target.body = self.myTextView.text;
target.time = [NSDate date];
}
[stack saveContext];
}
在 table 视图中,当我获取数据时,我是这样做的:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellIdentifier = @"StackTableViewCell";
Target *target = [self.fetchedResultController objectAtIndexPath:indexPath];
StackTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (!cell)
{
NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:@"StackTableViewCell" owner:self options:nil];
cell = [topLevelObjects objectAtIndex:0];
}
cell.cellLabel.text = target.body;
cell.cellLabel.font = [UIFont fontWithName:@"Candara-Bold" size:20];
cell.showsReorderControl = YES;
// Configure the cell...
return cell;
}
这是我在 table 视图控制器中的 fetchresultconroller/fetch 请求配置 class:
- (NSFetchRequest *)targetsFetchRequest {
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Target"];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"time" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
return fetchRequest;
}
- (NSFetchedResultsController *)fetchedResultController {
if (_fetchedResultController != nil) {
return _fetchedResultController;
}
CoreDataStack *stack = [CoreDataStack defaultStack];
NSFetchRequest *fetchRequest = [self targetsFetchRequest];
_fetchedResultController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:stack.managedObjectContext sectionNameKeyPath:nil cacheName:nil];
_fetchedResultController.delegate = self;
return _fetchedResultController;
}
我想要完成的是,每当用户创建一个目标对象时,它都会到达数组的末尾(所以它就像一个队列),如果用户移动单元格,那么我需要更改数据库数组的顺序...
移动单元格方法:
- (void)moveTableView:(FMMoveTableView *)tableView moveRowFromIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath {
int start = 0;
int end = 0;
if (fromIndexPath.row > toIndexPath.row) {
start = (int)fromIndexPath.row;
end = (int)toIndexPath.row;
} else {
start = (int)toIndexPath.row;
end = (int)fromIndexPath.row;
}
for (int i = start; i <= end; ++i) {
Target *target = [self.fetchedResultController objectAtIndexPath:[NSIndexPath indexPathForRow:i inSection:0]];
[target setOrder:@(i)];
}
[[CoreDataStack defaultStack] saveContext];
// a test to see if the order is changed
[self.fetchedResultController performFetch:nil];
NSArray *arr = [self.fetchedResultController fetchedObjects];
for (int i=0; i<arr.count; i++) {
Target *ta = [arr objectAtIndex:i];
NSLog(@"%@",ta.body);
}
}
日志:
2015-04-14 10:29:13.405 Lister[3163:477453] One
2015-04-14 10:29:13.406 Lister[3163:477453] Two
2015-04-14 10:29:13.406 Lister[3163:477453] Three
2015-04-14 10:29:13.407 Lister[3163:477453] Four
2015-04-14 10:29:13.407 Lister[3163:477453] Five
2015-04-14 10:29:21.070 Lister[3163:477453]
2015-04-14 10:29:21.071 Lister[3163:477453] One
2015-04-14 10:29:21.071 Lister[3163:477453] Two
2015-04-14 10:29:21.071 Lister[3163:477453] Three
2015-04-14 10:29:21.072 Lister[3163:477453] Four
2015-04-14 10:29:21.072 Lister[3163:477453] Five
2015-04-14 10:29:25.037 Lister[3163:477453]
2015-04-14 10:29:25.039 Lister[3163:477453] One
2015-04-14 10:29:25.039 Lister[3163:477453] Two
2015-04-14 10:29:25.040 Lister[3163:477453] Three
2015-04-14 10:29:25.040 Lister[3163:477453] Four
2015-04-14 10:29:25.041 Lister[3163:477453] Five
此外,单元格的标签现在表现得很奇怪,如果将带有标签 "one" 的单元格移动到带有标签 "two" 的单元格的索引处,那么 [=39] 的标签=] 正在更改为 "two"。所以我遇到了 2 个单元格具有相同标签的情况。
如果您希望 NSFetchResultsController 接受更改,则必须更改基础数据模型以反映新订单。
嗯,最简单的解决方案是
- 向您的
Target
实体添加一个属性,例如order
类型Integer32
。
创建和插入新对象
每当您创建一个新的
Target
对象时,首先使用具有键@"order"
和ascending=YES
的sortDescriptor
从数据库中获取现有对象。获取此获取数组的最后一个对象并检查其顺序。现在在您的新Target
对象中增加订单并将其插入数据库。如果取出数组returns0个对象,则设置order=@(0)
.- (void)insertTeget { CoreDataStack *stack = [CoreDataStack defaultStack]; //Fetching objects from database NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Target"]; NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"order" ascending:YES]; [fetchRequest setSortDescriptors:@[sortDescriptor]]; NSArray *existingObjects = [stack.managedObjectContext executeFetchRequest:fetchRequest error:nil]; //Creating new object Target *target = [NSEntityDescription insertNewObjectForEntityForName:@"Target" inManagedObjectContext:stack.managedObjectContext]; if (self.myTextView.text != nil) { target.body = self.myTextView.text; target.order = @([(Target *)existingObjects.lastObject order].integerValue + 1); } [stack saveContext]; }
NSFetchedResultsController
- 使用上面定义的
sortDescriptor
获取对象。
Taken from your code
- (NSFetchRequest *)targetsFetchRequest {
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Target"];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"order" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
return fetchRequest;
}
- (NSFetchedResultsController *)fetchedResultController {
if (_fetchedResultController != nil) {
return _fetchedResultController;
}
CoreDataStack *stack = [CoreDataStack defaultStack];
NSFetchRequest *fetchRequest = [self targetsFetchRequest];
_fetchedResultController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:stack.managedObjectContext sectionNameKeyPath:nil cacheName:nil];
_fetchedResultController.delegate = self;
return _fetchedResultController;
}
重新排列单元格
现在,当您在 table 视图中重新排列单元格时,您只需要 运行 一个 for 循环并更新它们的顺序。您只需要更新两个索引路径之间的
order
个对象。- (void)moveTableView:(FMMoveTableView *)tableView moveRowFromIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath { int start = 0; int end = 0; if (fromIndexPath.row > toIndexPath.row) { start = fromIndexPath.row; end = toIndexPath.row; } else { start = toIndexPath.row; end = fromIndexPath.row; } for (int i = start; i <= end; ++i) { Target *target = [self.fetchedResultsController objectAtIndexPath:[NSIndexPath indexPathForRow:i inSection:0]]; [target setOrder:@(i)]; } [[CoreDataStack defaultStack] saveContext]; }
注意:上述解决方案假设您有order
从0开始。
当您创建和插入新的 Target
对象时,您需要实施 NSFetchedResultsController
委托方法来为这些对象添加相应的行。由于我们已经定义了 sortDescriptor
,新行将添加到 tableView
.
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
[self.tableView beginUpdates];
}
- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo
atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type {
switch(type) {
case NSFetchedResultsChangeInsert:
[self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex]
withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject
atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type
newIndexPath:(NSIndexPath *)newIndexPath {
UITableView *tableView = self.tableView;
switch(type) {
case NSFetchedResultsChangeInsert:
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]
withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeUpdate:
break;
case NSFetchedResultsChangeMove:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
withRowAnimation:UITableViewRowAnimationFade];
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]
withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
[self.tableView endUpdates];
}
简单的解决方案: Reffer Apple's Doc:
创建一个 NSMutableArray 来识别重新排序的数组。
第 1 步: 在 header 或 class 文件中声明一个 NSMutableArray
属性 @property (strong, nonatomic) NSMutableArray *arrayTag
。
步骤 2: 在 viewDidLoad
步骤 3: 在 tableview
委托方法中添加此代码
-(void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath
{
NSString *stringToMove = [arrayTag objectAtIndex:sourceIndexPath.row];
[arrayTag removeObjectAtIndex:sourceIndexPath.row];
[arrayTag insertObject:stringToMove atIndex:destinationIndexPath.row];
}
试试这个
-(void)moveTableView:(UITableView *)tableView moveRowFromIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath {
NSString *str1 = [[yourArray objectAtIndex:fromIndexPath.row] copy];
NSString *str2 = [[yourArray objectAtIndex:toIndexPath.row] copy];
[yourArray replaceObjectAtIndex:fromIndexPath.row withObject:str2];
[yourArray replaceObjectAtIndex:toIndexPath.row withObject:str1];
[tableView reloadData];
}
如果我正确理解了你的问题,你必须更改模型的架构。
一个。一、我的理解
您有一个项目列表。最后添加新项目。现在您想让用户能够以自定义方式重新订购商品。
乙。你做什么
实际上您正在使用创建日期属性进行订购。当然你不能用它来重新排序,因为这意味着改变创建日期。因此,使用创建日期的整个方法都失败了:对于用户未更改的列表来说已经足够了,但如果您有自定义订单,则不行。
摄氏度。你能做什么
如果您有自定义订单,则需要自定义属性来反映订单。如果列表是实例对象的 属性,您可以使用 NSOrderedSet
和 Core Data 的有序关系来这样做。我不会因为回报。但是,如果适合您,您可以这样做。
否则你必须自己处理:
一个。将属性 order
添加到您的实体类型。
b。当你插入一个新对象时,检查现有列表的计数(取决于你如何持有它)并将值设置为新实例的order
属性。
c。抓取的时候,用那个属性排序。
d。更改时,更改实例对象的属性以及源和目标之间的所有实例对象的属性。让我解释一下:
我们有这样的 lis:
name order
Amin 0
Negm 1
Awad 2
Answer 3
现在,例如,Answer 被向上 从位置 3 移动到位置 1(在 Negm 之前):
name order
Amin 0
Answer 3
Negm 1
Awad 2
这意味着必须将移动对象 (3) 的顺序属性更改为新目标 (1),并且必须将顺序属性为 >=1 到 <3 的所有对象的顺序属性更改为 + 1. (显然,移动对象的顺序属性也是如此)
name order
Amin 0
Answer 1
Negm 2
Awad 3
在代码中
NSUInteger oldIndex = …; // 3
NSUInteger newIndex = …; // 1
movedObject.order = newIndex;
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Target"];
NSPredicate *betweenPredicate = [NSPredicate predicateWithFormat:@"order >= %ld AND order < %ld", newIndex, oldIndex];
NSArray *objectsToChange = [context executeFetchRequest:request error:NULL];
for( Target *target in objectsToChange )
{
target.order = @([target.order unsignedIntegerValue] + 1);
}
如果一个项目被向下移动,你必须反过来做同样的事情。
如果您有不同的唯一对象列表,例如 iTunes 中的播放列表,您需要一个额外的实体类型而不是一个额外的属性。让我知道,我将 post 我的一本书中的代码,包括移动一个空白的项目列表。