使用FetchedResultsController在TextField下面使用Core Data中的条目自动填充表视图

时间:2013-11-25 10:04:15

标签: ios objective-c core-data uitextfield auto-populate

我正在开发第一个应用程序,需要一些帮助。我已经阅读了大量关于SO的类似问题,但却没有得到任何结论。

我有一个简单的表视图控制器,它有一个加号按钮;按下时,会导致模态视图控制器要求用户将信息插入4个单独的字段中。当用户单击“保存”时,模式视图将被解除,并且信息将显示在表视图中,因为保存按钮调用NSManagedObject子类并通过Core Data保存它。

我正试图让它在用户键入第一个字段(名称)时,如果他们之前已经输入过该名称(如果他们使用save方法将其添加到Core Data),它会自动填充并显示一个隐藏的表视图,其中的条目与该名称匹配。我首先开始使用NSMutableArray,但感谢Jeff的评论,这不会持久保存数据,所以因为我已经拥有了Core Data功能,所以使用它更有意义。我正在编辑这篇文章,以包括我的核心数据目前的设置方式。

我基本上想用Core Data(http://www.dalmob.org/2011/03/01/alternative-autocomplete-uitextfield/

来实现这个目标

有一个信息实体与人物实体有关系。

- (IBAction)save:(id)sender
{
    NSManagedObjectContext *context = [self managedObjectContext];
    Information *information = [NSEntityDescription insertNewObjectForEntityForName:@"Information" inManagedObjectContext:context];

    People *enteredPerson = (People *)[People personWithName:self.nameTextField.text inManagedObjectContext:context];
    information.whichPerson = enteredPerson;
    NSError *error = nil;
    if (![context save:&error])
    {
        NSLog(@"Can't save! %@ %@", error, [error localizedDescription]);
    }
    [self dismissViewControllerAnimated:YES completion:nil];
}

enteredPerson调用People NSManagedObjectSubclass中的personWithName方法:

+ (People *)personWithName:(NSString *)name inManagedObjectContext:(NSManagedObjectContext *)context
{
    People *people = nil;

    // Creating a fetch request to check whether the name of the person already exists
    NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"People"];
    request.predicate = [NSPredicate predicateWithFormat:@"name = %@", name];
    NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"name" ascending:YES];
    request.sortDescriptors = [NSArray arrayWithObject:sortDescriptor];

    NSError *error = nil;
    NSArray *fetchedPeople = [context executeFetchRequest:request error:&error];
    if (!fetchedPeople)
    {
        // Handle Error
    }
    else if (![fetchedPeople count])
    {
        // If the person count is 0 then let's create it
        people = [NSEntityDescription insertNewObjectForEntityForName:@"People" inManagedObjectContext:context];
        people.name = name;
    }
    else
    {
        // If the object exists, just return the last object .
        people = [fetchedPeople lastObject];
    }
    return people; 
}

基于创建NSFetchRequest的建议,我想知道最好的技术来做到这一点。

我是否在最后添加条目的保存方法中执行此操作:

// NSFetchRequest

NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Person" inManagedObjectContext:context];
[fetchRequest setEntity:entity];

// Specifiy a predicate here if there are certain conditions your fetch must adhere to
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"ANY name CONTAINS[c] %@", self.nameTextField.text];
[fetchRequest setPredicate:predicate];

//NSError *error = nil;
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
if (fetchedObjects == nil) {
    // Handle error
}
if ([fetchedObjects count] == 0)
{
    // Add entry to results
}

我想要实现的是,从Core Data,当用户键入名称,引用核心数据(带有获取请求)时,如果该名称存在,则在用户开始输入时,填充下面的Table视图文本字段。

任何指导都将不胜感激。

编辑:我已经用一些其他代码更新了答案,几乎可以实现这一点。

编辑:更多代码:

.h

中的属性声明
@property (retain, nonatomic) IBOutlet UITextField *nameTextField;
@property (nonatomic, retain) NSString *substring;
@property (weak, nonatomic) IBOutlet UITableView *testTableView;
@property (nonatomic, retain) NSFetchedResultsController* autocompleteFetchedResultsController;

- (void)searchAutocompleteEntriesWithSubstring:(NSString *)substring;

viewDidLoad中

- (void)viewDidLoad
{
    NSError *error;
    if (![[self autocompleteFetchedResultsController] performFetch:&error])
    {
        NSLog(@"Unresolved error %@ %@", error, [error userInfo]);
        exit(-1);
    }
    self.testTableView.delegate = self;
    self.testTableView.dataSource = self;
    self.testTableView.hidden = YES;
    self.testTableView.scrollEnabled = YES;
    self.nameTextField.delegate = self;
    [super viewDidLoad];
}

保存方法

- (IBAction)save:(id)sender
{
    NSManagedObjectContext *context = [self managedObjectContext];
    Transaction *transaction = [NSEntityDescription insertNewObjectForEntityForName:@"Transaction" inManagedObjectContext:context];    
    People *enteredPerson = (People *)[People personWithName:self.nameTextField.text inManagedObjectContext:context];
    transaction.whoFrom = enteredPerson;
    NSError *error = nil;
    if (![context save:&error])
    {
        NSLog(@"Can't save! %@ %@", error, [error localizedDescription]);
    }
    [self dismissViewControllerAnimated:YES completion:nil];
}

谢谢,

3 个答案:

答案 0 :(得分:0)

使用Xcode片段库中的基本代码,您可以执行核心数据提取:

NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"<#Entity name#>" inManagedObjectContext:<#context#>];
[fetchRequest setEntity:entity];

// Specifiy a predicate here if there are certain conditions your fetch must adhere to
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"<#Predicate string#>", <#Predicate arguments#>];
[fetchRequest setPredicate:predicate];

NSError *error = nil;
NSArray *fetchedObjects = [<#context#> executeFetchRequest:fetchRequest error:&error];
if (fetchedObjects == nil) {
    // Handle error
}

将实体名称替换为存储NameTextField条目的名称。 fetchedObjects是一个数组,用于存储填充表格所需的信息。

显然,您还需要通过创建新实体并保存上下文,将任何新的NameTextField条目保存到核心数据。

答案 1 :(得分:0)

我有点工作了。我没有更新整个问题,而是将其留在那里供参考,因为我相信有人会遇到类似的情况。通过在我的视图控制器中使用FetchedResultsController对象,我现在得到一个名称列表,用于填充位于文本字段下方的表视图。

让我们看看一些代码:

- (NSFetchedResultsController *)autocompleteFetchedResultsController
{
    NSManagedObjectContext *managedObjectContext = [self managedObjectContext];
    if (_autocompleteFetchedResultsController != nil)
    {
        return _autocompleteFetchedResultsController;
    }
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];

    NSEntityDescription *entity = [NSEntityDescription entityForName:@"People" inManagedObjectContext:managedObjectContext];
    fetchRequest.entity = entity;

    if ([self.substring length] > 0) {
        NSPredicate *peoplePredicate = [NSPredicate predicateWithFormat:@"ANY name CONTAINS[c] %@", self.nameTextField.text];

        [fetchRequest setPredicate:personPredicate];
    }

    NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:NO];
    fetchRequest.sortDescriptors = [NSArray arrayWithObject:sort];
    NSFetchedResultsController *theFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:managedObjectContext sectionNameKeyPath:nil cacheName:nil];
    self.autocompleteFetchedResultsController = theFetchedResultsController;    _autocompleteFetchedResultsController.delegate = self;
    return _autocompleteFetchedResultsController;
}

- (void)viewDidLoad
{

    NSError *error;
    // I am performing the fetchHere and if there is an error, it will get logged.
    if (![[self autocompleteFetchedResultsController] performFetch:&error])
    {
        NSLog(@"Unresolved error %@ %@", error, [error userInfo]);
        exit(-1);
    }

    // Further code relating to tableview to make it hidden, etc
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    id  sectionInfo = [[_autocompleteFetchedResultsController sections] objectAtIndex:section];
    return [sectionInfo numberOfObjects];
}

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
    self.testTableView.hidden = NO;
    self.substring = self.nameTextField.text];
    self.substring = [self.substring stringByReplacingCharactersInRange:range withString:self.substring];
    [self searchAutocompleteEntriesWithSubstring:self.substring];
    return YES;
}

#pragma mark UITableViewDataSource methods


- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"autocomplete cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
    People *people = [self.autocompleteFetchedResultsController objectAtIndexPath:indexPath];
    cell.textLabel.text = people.name;
    return cell;
}

#pragma mark UITableViewDelegate methods

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {

    UITableViewCell *selectedCell = [tableView cellForRowAtIndexPath:indexPath];
    self.nameTextField.text = selectedCell.textLabel.text;

}

所以这在某种程度上起作用。当我将光标放在nameTextField中时,它取消隐藏表视图,但它当前显示已输入的所有名称的名称。

我想要的是,正如我打字一样,桌子只能向我显示匹配的内容。

[self searchAutocompleteEntriesWithSubstring:substring];中的shouldChangeCharactersInRangeMethod正在调用我创建的自定义方法。

当我将此设置为NSMutableArray而不是使用Core Data时,它是下面的代码,但我不知道如何调整此代码来说,搜索核心数据并仅显示与我已经匹配的结果打字。

- (void)searchAutocompleteEntriesWithSubstring:(NSString *)substring {

        self.autocompleteFetchedResultsController = nil;
        [self autocompleteFetchedResultsController];
        [self.testTableView reloadData];
}

我几乎就在那里 - 只需要一点推动就到那儿!

答案 2 :(得分:0)

我猜self.autocompleteUrls是你以前的NSMutableArray ...好的,你已经走了很长的路,现在看到autocompleteFetchedResultsController - &gt;这就是取物,并且条件if(_autocompleteFetchedResultsController!= nil)保护属性方法不会在每次U引用autocompleteFetchedResultsController时被调用。所以你应该做这样的事情:

- (void)searchAutocompleteEntriesWithSubstring:(NSString *)substring {
    _autocompleteFetchedResultsController = nil;
    [self autocompleteFetchedResultsController];
    [self.testTableView reloadData];
}

如果你做了其他正确的事情应该是它......

您的cellFoRowAtIndexPath应如下所示:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"autocomplete cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];

    if(cell == nil){
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewStylePlain reuseIdentifier:CellIdentifier];
    }

    People *people = [self.autocompleteFetchedResultsController objectAtIndexPath:indexPath];
    cell.textLabel.text = people.name;
    return cell;
}