iOS Core Data谓词,用于根据相关数据进行过滤

时间:2014-07-03 23:09:47

标签: ios entity-framework core-data nspredicate nsfetchrequest

好吧,我是一个谓词菜鸟。他们对我来说很陌生。

关于应用程序:

我有一款可以处理游戏比赛的应用。有球员,签到和比赛的实体。 这个想法是玩家被添加到应用程序,然后可以签入进行播放,并存储匹配结果。

关系:

玩家< - >> CheckIns(每个玩家可以在不同日期进行多次签到)

  • 来自:玩家实体
  • 关系:playerCheckins
  • 反向:checkedInPlayer
  • 目的地:CheckIn实体

玩家<< - >>比赛(每场比赛可以有两名球员,每次比赛时球员可以有多场比赛)

  • 来自:玩家实体
  • 关系:playerMatches
  • 反向:matchPlayers
  • 目的地:匹配实体

我有一个共享集合视图,列出了应用中的所有玩家。当玩家登记时以及将它们添加到新的匹配条目时使用它。到目前为止,这一切都很好。

我想做什么:

我希望玩家收藏视图根据他们的登记状态过滤列出的玩家。例如,在检入新玩家时,玩家收集视图应仅显示当天尚未检查的玩家。 (CheckIns每个条目都有一个日期属性)此外,在向匹配项添加玩家时,集合视图应该只显示那天已经检查的玩家。

我计划在玩家集合视图中添加一个NSString属性,当我以模态方式加载集合视图时,该视图使用谓词文本设置。这样,我可以根据我是否从我的匹配中调用集合视图并分别检入视图来更改谓词。

这可能与我想做的事情有关吗?这些谓词字符串会是什么样的?

谢谢!

更新

我应该更清楚。我正在使用fetchedresultscontroller让玩家进入集合视图,所以我正在寻找在获取请求中使用的谓词......

添加代码......

我的CollectionViewController:

PlayersCollectionViewController.h
#import "CoreCollectionViewController.h"
#import "Player.h"

@protocol PlayersCollectionViewControllerDelegate;


@interface PlayersCollectionViewController : CoreCollectionViewController <NSFetchedResultsControllerDelegate>

@property (nonatomic, assign) id <PlayersCollectionViewControllerDelegate> delegate;
@property (nonatomic, strong) NSString *titleText;
@property (nonatomic, strong) NSString *predicate;

- (IBAction)cancel:(UIBarButtonItem *)sender;
- (IBAction)done:(UIBarButtonItem *)sender;

@end

@protocol PlayersCollectionViewControllerDelegate <NSObject>

@optional
-(void)ViewController:(UIViewController *)sender
     didSelectPlayer:(Player *)selectedPlayer;

@optional
-(void)ViewController:(UIViewController *)sender
      didSelectPlayer1:(Player *)selectedPlayer1;

@optional
-(void)ViewController:(UIViewController *)sender
      didSelectPlayer2:(Player *)selectedPlayer2;

@end

......和实施:

#import "PlayersCollectionViewController.h"
#import "AppDelegate.h"
#import "PlayerCollectionViewCell.h"

@interface PlayersCollectionViewController ()
@property(nonatomic, strong) NSManagedObjectContext *managedObjectContext;
@property (nonatomic, strong) NSFetchedResultsController *fetchedResultsController;
@end

@implementation PlayersCollectionViewController
@synthesize titleText, predicate;

static NSString * const reuseIdentifier = @"Cell";

- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        // Custom initialization
    }
    return self;
}

-(NSManagedObjectContext*)managedObjectContext{
    return [(AppDelegate*)[[UIApplication sharedApplication]delegate]managedObjectContext];
}

- (void)viewDidLoad
{
    [super viewDidLoad];

    // Uncomment the following line to preserve selection between presentations
    // self.clearsSelectionOnViewWillAppear = NO;

    // Register cell classes
    [self.collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:reuseIdentifier];

    // Do any additional setup after loading the view.
    NSError *error = nil;
    if (![[self fetchedResultsController]performFetch:&error]) {
        NSLog(@"Error: %@", error);
        abort();
    }
}

-(void)viewWillAppear:(BOOL)animated
{
    [self setTitle:titleText];
    [self.collectionView reloadData];
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

/*
#pragma mark - Navigation

// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    // Get the new view controller using [segue destinationViewController].
    // Pass the selected object to the new view controller.
}
*/

#pragma mark <UICollectionViewDataSource>

- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
{
    return [[self.fetchedResultsController sections]count];
}


- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
    return [[self.fetchedResultsController fetchedObjects]count];
}

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{

    PlayerCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"PlayerCollCell" forIndexPath:indexPath];
    Player *player = [self.fetchedResultsController objectAtIndexPath:indexPath];

    // Configure the cell

    if (player.playerImage) {
        cell.playerImageView.image = [UIImage imageWithContentsOfFile:player.playerImageSmall];
    }

    [cell.playerNameLabel setText:player.firstName];

    return cell;
}

-(BOOL)collectionView:(UICollectionView *)collectionView shouldHighlightItemAtIndexPath:(NSIndexPath *)indexPath
{
    return YES;
}

-(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{

    PlayerCollectionViewCell *selectedPlayerCell = (PlayerCollectionViewCell*)[collectionView cellForItemAtIndexPath:indexPath];

    [collectionView dequeueReusableCellWithReuseIdentifier:@"PlayerCollCell" forIndexPath:indexPath];

    selectedPlayerCell.backgroundColor = [UIColor lightGrayColor];

    Player *selectedPlayer = [self.fetchedResultsController objectAtIndexPath:indexPath];

    NSLog(@"Selected player %@", selectedPlayer.firstName);

    if ([self.title isEqualToString:@"Select Player 1"]) {
        [self.delegate ViewController:self didSelectPlayer1:selectedPlayer];
    } else if ([self.title isEqualToString:@"Select Player 2"]) {
        [self.delegate ViewController:self didSelectPlayer2:selectedPlayer];
    } else if ([self.title isEqualToString:@"Select Player"]) {
        [self.delegate ViewController:self didSelectPlayer:selectedPlayer];
    }

    [self dismissViewControllerAnimated:YES completion:nil];
}

#pragma mark - ViewController methods
- (IBAction)cancel:(UIBarButtonItem *)sender {
    [self dismissViewControllerAnimated:YES completion:nil];
}

- (IBAction)done:(UIBarButtonItem *)sender { //not sure if this method and UI are needed...
    [self dismissViewControllerAnimated:YES completion:nil];
}


#pragma mark - Fetched Results Controller Section
-(NSFetchedResultsController*) fetchedResultsController
{
    if (_fetchedResultsController !=nil) {
        return _fetchedResultsController;
    }

    NSFetchRequest *fetchedRequest = [[NSFetchRequest alloc]init];

    NSManagedObjectContext *context = [self managedObjectContext];

    NSEntityDescription *entity =[NSEntityDescription entityForName:@"Player" inManagedObjectContext:context];

    [fetchedRequest setEntity:entity];

    NSSortDescriptor *lastNameSortDescriptor = [[NSSortDescriptor alloc]initWithKey:@"lastName" ascending:YES];
    NSSortDescriptor *firsttNameSortDescriptor = [[NSSortDescriptor alloc]initWithKey:@"firstName" ascending:YES];

    NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:lastNameSortDescriptor, firsttNameSortDescriptor, nil];

    fetchedRequest.sortDescriptors = sortDescriptors;


    _fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchedRequest
                                                                    managedObjectContext:context
                                                                      sectionNameKeyPath:nil
                                                                               cacheName:nil];

    _fetchedResultsController.delegate = self;

    return _fetchedResultsController;

}

#pragma mark - Fetched Results Controller Delegates
/*
 -(void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
 [self.collectionView beginUpdates];
 }

 -(void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
 [self.collectionView endUpdates];
 }
 */

-(void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath {

    UICollectionView *collectionView = self.collectionView;

    switch (type) {
        case NSFetchedResultsChangeInsert:
            [collectionView insertItemsAtIndexPaths:[NSArray arrayWithObjects:newIndexPath, nil]];
            break;

        case NSFetchedResultsChangeDelete: [collectionView deleteItemsAtIndexPaths:[NSArray arrayWithObjects:indexPath, nil]];
            break;

        case NSFetchedResultsChangeUpdate: {
            PlayerCollectionViewCell *selectedPlayerCell = (PlayerCollectionViewCell*)[collectionView cellForItemAtIndexPath:indexPath];

            //selectedPlayerCell.backgroundColor = [UIColor lightGrayColor];

            Player *selectedPlayer = [self.fetchedResultsController objectAtIndexPath:indexPath];

            if (selectedPlayer.playerImage) {
                selectedPlayerCell.playerImageView.image = [UIImage imageWithContentsOfFile:selectedPlayer.playerImageSmall];
            }

            [selectedPlayerCell.playerNameLabel setText:selectedPlayer.firstName];

        }
            break;

        case NSFetchedResultsChangeMove: [collectionView deleteItemsAtIndexPaths:[NSArray arrayWithObjects:indexPath, nil]];
            [collectionView insertItemsAtIndexPaths:[NSArray arrayWithObjects:newIndexPath, nil]];
            break;

    }

}

@end

2 个答案:

答案 0 :(得分:2)

暂时离开视图控制器并取出结果控制器。您的谓词属于模型层,而不属于Controller或View层。 NSFetchRequest或NSFetchedResultsController的谓词没什么特别之处。它仍然只是一个NSPredicate。

为什么在Model层?您基于“当天检查”的过滤问题与视图或显示给用户的内容无关。这纯粹是一个数据问题。所以在Model层解决它(奖励:现在很容易为这个fetch写一个单元测试)。

如果你作弊并使用你对数据的了解,你可以更容易。使播放器&lt; - &gt;&gt; CheckIn关系成为有序的(注意:核心数据中的实体名称是单数,就像类名一样,尽管多对多关系是复数)。现在很容易获得玩家最近的签到:它是thePlayer.checkins[0],签到时间是[thePlayer.checkins[0] timeOfEvent]。只需确保在向CheckIn添加Player时,将其放在索引0处。

但是,您可以通过对数据模型进行非规范化来使其变得更加简单。保持从PlayerCheckIn的多对多关系。但是在PlayertimeOfLastCheckIn上添加了一个属性。确保在添加CheckIn时更新timeOfLastCheckIn; “说一次”意味着你应该在方法中包装这对步骤,并且总是调用该方法来添加CheckIn。

现在到Xcode Data Modeler。使用替换变量在Player上创建新的提取请求。在下拉列表中选择表达式,然后输入timeOfLastCheckIn > $fromdate。在Player+playersCheckingInAfterDate:context:)上编写一个方法来执行该获取请求,接受两个参数NSManagedObjectContextNSDate,并返回Players数组。您将使用fetchRequestFromTemplateWithName:substitutionVariables:来实例化获取请求,将当前午夜对应的NSDate替换为$fromdate。如果您愿意,可以添加一个包装器方法来计算当地午夜的时间并将其传递给+playersCheckingInAfterDate:context:。您可以在类似的方法/获取模板集中将>切换为<,以查找今天未登记过的玩家。

答案 1 :(得分:0)

我不确定您的签到状态是如何运作的。假设它是BOOL,请执行以下操作:

NSPredicate *predicate = [NSPredicate predicateWithFormat:@"checkedInStatus == 1"];