草庐IT

用于根据相关数据进行过滤的 iOS Core Data Predicate

coder 2024-01-19 原文

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

关于应用程序:

我有一个处理游戏比赛的应用程序。有用于玩家、签到和比赛的实体。 这个想法是将球员添加到应用程序,然后可以登记参加比赛,并存储比赛结果。

关系:

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

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

选手 <->> 比赛(每场比赛可以有两名选手,选手每次比赛可以有多场比赛)

  • 来自:玩家实体
  • 关系:playerMatches
  • 逆向:matchPlayers
  • 目标:匹配实体

我有一个共享 Collection View ,其中列出了应用中的所有玩家。当玩家签到以及将他们添加到新的比赛条目时使用它。到目前为止一切正常。

我想做什么:

我希望玩家 Collection View 能够根据签到状态过滤列出的玩家。例如,签到新玩家时,玩家 Collection View 应该只显示当天尚未签到的玩家。 (CheckIns 对每个条目都有一个日期属性)此外,在将玩家添加到比赛中时, Collection View 应仅显示当天已经签到的玩家。

我计划在模态加载 Collection View 时将 NSString 属性添加到使用谓词文本设置的玩家 Collection View 。这样,我可以根据我是否分别从我的匹配和 checkin View 中调用 Collection View 来更改谓词。

这对我正在尝试做的事情可能吗?这些谓词字符串会是什么样子?

谢谢!

更新

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

添加代码...

我的收藏 View Controller :

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

最佳答案

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

为什么要在模型层做这个?您基于“当天签到”进行过滤的问题与 View 或向用户显示的内容无关。这纯粹是一个数据问题。所以在模型层解决它(好处:现在很容易为这个获取编写单元测试)。

如果您稍微作弊并使用您对数据的了解,您可以使这更容易。使 Player<->>CheckIn 关系有序(旁注:Core Data 中的实体名称是单数,就像类名一样,尽管对多关系是复数)。现在很容易获得玩家最近的签到:它是 thePlayer.checkins[0] , 签到时间是[thePlayer.checkins[0] timeOfEvent] .只要确保在添加 CheckIn 时到 Player ,你把它放在索引 0 处。

不过,您可以通过稍微非规范化您的数据模型来使其更容易。保持来自 Player 的一对多关系至 CheckIn .但是在 Player 上添加一个属性, timeOfLastCheckIn .确保更新 timeOfLastCheckIn每当您添加签到时; “说一次”意味着您应该将这对步骤包装在一个方法中,并始终调用该方法来添加 CheckIn。

现在进入 Xcode 数据建模器。在 Player 上创建一个新的获取请求带有替代变量。在下拉列表中选择表达式并输入 timeOfLastCheckIn > $fromdate .在 Player 上写一个方法( +playersCheckingInAfterDate:context: ) 执行该获取请求,接受两个参数, NSManagedObjectContext和一个 NSDate ,并返回 Players 的数组.您将使用 fetchRequestFromTemplateWithName:substitutionVariables:实例化一个获取请求,将对应于本地午夜的 NSDate 替换为 $fromdate .添加一个包装器方法,如果你愿意,计算本地午夜时间并将其传递给 +playersCheckingInAfterDate:context: .您可以切换 ><在一组类似的方法/获取模板中找到今天没有签到的玩家。

关于用于根据相关数据进行过滤的 iOS Core Data Predicate,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24564551/

有关用于根据相关数据进行过滤的 iOS Core Data Predicate的更多相关文章

  1. ruby-on-rails - 使用 Ruby on Rails 进行自动化测试 - 最佳实践 - 2

    很好奇,就使用ruby​​onrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提

  2. ruby-on-rails - 按天对 Mongoid 对象进行分组 - 2

    在控制台中反复尝试之后,我想到了这种方法,可以按发生日期对类似activerecord的(Mongoid)对象进行分组。我不确定这是完成此任务的最佳方法,但它确实有效。有没有人有更好的建议,或者这是一个很好的方法?#eventsisanarrayofactiverecord-likeobjectsthatincludeatimeattributeevents.map{|event|#converteventsarrayintoanarrayofhasheswiththedayofthemonthandtheevent{:number=>event.time.day,:event=>ev

  3. ruby-on-rails - Rails 常用字符串(用于通知和错误信息等) - 2

    大约一年前,我决定确保每个包含非唯一文本的Flash通知都将从模块中的方法中获取文本。我这样做的最初原因是为了避免一遍又一遍地输入相同的字符串。如果我想更改措辞,我可以在一个地方轻松完成,而且一遍又一遍地重复同一件事而出现拼写错误的可能性也会降低。我最终得到的是这样的:moduleMessagesdefformat_error_messages(errors)errors.map{|attribute,message|"Error:#{attribute.to_s.titleize}#{message}."}enddeferror_message_could_not_find(obje

  4. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  5. ruby - 使用 C 扩展开发 ruby​​gem 时,如何使用 Rspec 在本地进行测试? - 2

    我正在编写一个包含C扩展的gem。通常当我写一个gem时,我会遵循TDD的过程,我会写一个失败的规范,然后处理代码直到它通过,等等......在“ext/mygem/mygem.c”中我的C扩展和在gemspec的“扩展”中配置的有效extconf.rb,如何运行我的规范并仍然加载我的C扩展?当我更改C代码时,我需要采取哪些步骤来重新编译代码?这可能是个愚蠢的问题,但是从我的gem的开发源代码树中输入“bundleinstall”不会构建任何native扩展。当我手动运行rubyext/mygem/extconf.rb时,我确实得到了一个Makefile(在整个项目的根目录中),然后当

  6. ruby - 如何进行排列以有效地定制输出 - 2

    这是一道面试题,我没有答对,但还是很好奇怎么解。你有N个人的大家庭,分别是1,2,3,...,N岁。你想给你的大家庭拍张照片。所有的家庭成员都排成一排。“我是家里的friend,建议家庭成员安排如下:”1岁的家庭成员坐在这一排的最左边。每两个坐在一起的家庭成员的年龄相差不得超过2岁。输入:整数N,1≤N≤55。输出:摄影师可以拍摄的照片数量。示例->输入:4,输出:4符合条件的数组:[1,2,3,4][1,2,4,3][1,3,2,4][1,3,4,2]另一个例子:输入:5输出:6符合条件的数组:[1,2,3,4,5][1,2,3,5,4][1,2,4,3,5][1,2,4,5,3][

  7. ruby - 如何根据特征实现 FactoryGirl 的条件行为 - 2

    我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden

  8. Ruby Sinatra 配置用于生产和开发 - 2

    我已经在Sinatra上创建了应用程序,它代表了一个简单的API。我想在生产和开发上进行部署。我想在部署时选择,是开发还是生产,一些方法的逻辑应该改变,这取决于部署类型。是否有任何想法,如何完成以及解决此问题的一些示例。例子:我有代码get'/api/test'doreturn"Itisdev"end但是在部署到生产环境之后我想在运行/api/test之后看到ItisPROD如何实现? 最佳答案 根据SinatraDocumentation:EnvironmentscanbesetthroughtheRACK_ENVenvironm

  9. ruby-on-rails - 相关表上的范围为 "WHERE ... LIKE" - 2

    我正在尝试从Postgresql表(table1)中获取数据,该表由另一个相关表(property)的字段(table2)过滤。在纯SQL中,我会这样编写查询:SELECT*FROMtable1JOINtable2USING(table2_id)WHEREtable2.propertyLIKE'query%'这工作正常:scope:my_scope,->(query){includes(:table2).where("table2.property":query)}但我真正需要的是使用LIKE运算符进行过滤,而不是严格相等。然而,这是行不通的:scope:my_scope,->(que

  10. ruby - 即使失败也继续进行多主机测试 - 2

    我已经构建了一些serverspec代码来在多个主机上运行一组测试。问题是当任何测试失败时,测试会在当前主机停止。即使测试失败,我也希望它继续在所有主机上运行。Rakefile:namespace:specdotask:all=>hosts.map{|h|'spec:'+h.split('.')[0]}hosts.eachdo|host|begindesc"Runserverspecto#{host}"RSpec::Core::RakeTask.new(host)do|t|ENV['TARGET_HOST']=hostt.pattern="spec/cfengine3/*_spec.r

随机推荐