ПРОГРАММИРОВАНИЕ ПОД IPHONE, IPAD OBJECTIVE-C часть 3

Менеджер задач

Я давно хотел написать подобный урок. В котором смог бы более полно рассказать о возможностях CoreData. Но никак не мог найти достойного задания. Хотелось написать урок, проект которого мог бы пригодится каждому и при это в нем было задействовано максимальное количество фишек работы с CoreData. Такое задание я нашел в одной из вакансий программиста iOS. Суть задачи заключается в написании приложения для ведения списка задач. Каждая задача имеет название, дату, время, фиксированную категорию, состояние (завершенная или нет). Кроме этого следует реализовать возможность редактировать задачи, удалять и создавать новые.

Интерфейс приложения состоит из трех вкладок:

 

  • На первой вкладке выводятся все задачи независимо от статуса;
  • На второй вкладке показывается список всех незавершенных задач;
  • На третьей — список завершенных задач.

 

Краткое тех-задание у нас есть, теперь приступим к созданию проекта. Для этого урока я выбрал шаблон Empty Application, проект назвал TaskManager, а непосредственно перед созданием поставил галочку Use Core Data.

 

 

Я предлагаю начать с внешнего вида нашего приложения. То есть, добавить TabBar с перечислеными выше вкладками. Поскольку каждая вкладка представляет из себя таблицу, а отличатся они будут лишь типом выводимых данных (точнее фильтром которым мы будем делать выборку нужных данных), я создал общий контроллер в котором они будут отображаться. Добавьте в проект новый класс с именем TasksViewController, который наследуется от UITableViewController. Как мы уже помним, этот контроллер просто выводит таблицу. Не будем пока что в нем ничего менять, а просто постараемся отобразить. Для этого внесем изменения в AppDelegate:

 

AppDelegate.h

<code data-result="[object Object]">#import &lt;UIKit/UIKit.h&gt;

@interface AppDelegate : UIResponder &lt;UIApplicationDelegate&gt;

@property (strong, nonatomic) UIWindow *window;

@property (readonly, strong, nonatomic) NSManagedObjectContext *managedObjectContext;
@property (readonly, strong, nonatomic) NSManagedObjectModel *managedObjectModel;
@property (readonly, strong, nonatomic) NSPersistentStoreCoordinator *persistentStoreCoordinator;
@property (strong, nonatomic) UITabBarController *tabBarController;

- (void)saveContext;
- (NSURL *)applicationDocumentsDirectory;

@end</code>

 

AppDelegate.m

<code data-result="[object Object]">#import "AppDelegate.h"
#import "TasksViewController.h"

@implementation AppDelegate

@synthesize window = _window;
@synthesize managedObjectContext = __managedObjectContext;
@synthesize managedObjectModel = __managedObjectModel;
@synthesize persistentStoreCoordinator = __persistentStoreCoordinator;
@synthesize tabBarController;

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];

    TasksViewController * allTasks = [[TasksViewController alloc] initWithNibName:@"TasksViewController" bundle:nil];
    allTasks.navigationItem.title = @"All";
    UINavigationController *all = [[UINavigationController alloc] initWithRootViewController: allTasks];
    all.title = allTasks.navigationItem.title;
    all.navigationBar.barStyle = UIBarStyleBlackOpaque;

    TasksViewController *pendingTsaks = [[TasksViewController alloc] initWithNibName:@"TasksViewController" bundle:nil];
    pendingTsaks.navigationItem.title = @"Pending";
    UINavigationController *pending = [[UINavigationController alloc] initWithRootViewController:pendingTsaks];
    pending.title = pendingTsaks.navigationItem.title;
    pending.navigationBar.barStyle = UIBarStyleBlackOpaque;

    TasksViewController *completedTsaks = [[TasksViewController alloc] initWithNibName:@"TasksViewController" bundle:nil];
    completedTsaks.navigationItem.title = @"Completed";
    UINavigationController *completed = [[UINavigationController alloc] initWithRootViewController:completedTsaks];
    completed.title = completedTsaks.navigationItem.title;
    completed.navigationBar.barStyle = UIBarStyleBlackOpaque;

    self.tabBarController = [[UITabBarController alloc] init];
    self.tabBarController.viewControllers = [NSArray arrayWithObjects:all, pending, completed, nil];

    self.window.rootViewController = self.tabBarController;

    self.window.backgroundColor = [UIColor whiteColor];
    [self.window makeKeyAndVisible];
    return YES;
}</code>

 

Я описал только метод didFinishLaunchingWithOptions, поскольку лишь в нем мы производим изменения. Если вы читали урок UITabBarController — у вас не возникнет вопросов о том, что же мы сделила. Результат работы должен выглядеть так:

 

 

Теперь подготовим модель данных нашей базы в которой будут храниться все записи. Исходя из ТЗ, нам нужны две таблицы, в первой мы будем хранить категории, а во второй сами записи. Кроме этого, следует наладить связь (один ко многим) между категориями и запиясми. Для этого перейдем к файлу TaskManager.xcdatamodeld и в нижней части экрана нажмем на кнопку Add Entity (надеюсь вы не забыли наше первое знакомство с CoreData). Назовем эту сущность Category и добавим ей два атрибута name строкового типа и timeStamp дата. Таким же образом добавим сущность Task, но кроме имени и метки добавим атрибут isCompleted (Boolean). Теперь важно правильно настроить связь (Relationship). Для этого выберите сущность Task и в поле связей (Relationships) нажмите на плюсик. Дайте имя связи category, а в поле Destination выберите сущность Category (поле Inverse пока что оставим пустым). Тоже самое проделайте для сущьности Category, но ее связь будет называться task, в поле Destination выберите сущность Task, а в Inverse — только что созданную свзяь category. После добавления связи task выберите ее и на вкладе утелит поставьте галочку To-Many Relationship.

 

 

Для облегчения работы с записями задач, я предлагаю создать сабкласс, поля которого будут соответсовать атрибутам сущьности. Для этого в перейдем в раздел CONFIGURATIONS и выберем его единственный объект Default. Нам мы увидим только что созданные две сущьности. Напротив сущьности Task в поле Class впишите такое же имя (Task).

 

 

Теперь следует добавить соответствующий класс. Эта процедура похожа на добавление обычного класса, только со списка шаблонов теперь нужно выбрать Core Data -> NSManagedObject subclass.

 

 

И соответственно, выбрать нужную сущность:

 

 

Теперь наша база полностью готова к использованию. Перейдем к процессу ее наполнения и вывода на экран ее содержимого. Поможет нам в этом NSFetchedResultsController, добавим вместе с методами делегата NSFetchedResultsControllerDelegateв TasksViewController:

 

TasksViewController.h

<code data-result="[object Object]">#import &lt;UIKit/UIKit.h&gt;

@interface TasksViewController : UITableViewController &lt;NSFetchedResultsControllerDelegate&gt;

@property (strong, nonatomic) NSFetchedResultsController *taskResults;

@end</code>

 

TasksViewController.m

<code data-result="[object Object]">#import "AppDelegate.h"
#import "TasksViewController.h"
#import "Task.h"

@interface TasksViewController ()
- (void)createFetchRequest;
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath;
@end

@implementation TasksViewController

@synthesize taskResults;

- (void)viewDidLoad
{
    [super viewDidLoad];

    self.navigationItem.leftBarButtonItem = self.editButtonItem;

    UIBarButtonItem *addButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd 
                                                                               target:self 
                                                                               action:@selector(createTask)];
    self.navigationItem.rightBarButtonItem = addButton;

    [self createFetchRequest];
}

- (void)createFetchRequest {
    AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
    NSManagedObjectContext *managedObjectContext = appDelegate.managedObjectContext;

    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    fetchRequest.entity = [NSEntityDescription entityForName:@"Task" 
                                      inManagedObjectContext:managedObjectContext];

    NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"timeStamp" ascending:NO];
    fetchRequest.sortDescriptors = [NSArray arrayWithObjects:sortDescriptor, nil];

    self.taskResults = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest 
                                                           managedObjectContext:managedObjectContext
                                                             sectionNameKeyPath:nil 
                                                                      cacheName:@"Master"];
    NSError *error = nil;
    if (![self.taskResults performFetch:&amp;error]) {
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
        abort();
    }
    self.taskResults.delegate = self;
}

- (void)createTask {
    AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
    NSManagedObjectContext *context = appDelegate.managedObjectContext;
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Task" inManagedObjectContext:context];

    Task *newNask = [NSEntityDescription insertNewObjectForEntityForName:[entity name] inManagedObjectContext:context];
    newNask.name = @"Example";
    newNask.isCompleted = [NSNumber numberWithBool:YES];    
    newNask.timeStamp = [NSDate date];
}

#pragma mark - Table view data source

- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath
{
    Task *task = [self.taskResults objectAtIndexPath:indexPath];
    cell.textLabel.text = task.name;
    cell.detailTextLabel.text = [task.timeStamp description];
    cell.accessoryType = task.isCompleted.boolValue? UITableViewCellAccessoryCheckmark:UITableViewCellAccessoryNone;
}

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return self.taskResults.sections.count;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    id &lt;NSFetchedResultsSectionInfo&gt; sectionInfo = [[self.taskResults sections] objectAtIndex:section];
    return [sectionInfo numberOfObjects];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
    }

    [self configureCell:cell atIndexPath:indexPath];

    return cell;
}

#pragma mark - NSFetchedResultsControllerDelegate

- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller
{
    [self.tableView beginUpdates];
}

- (void)controller:(NSFetchedResultsController *)controller 
  didChangeSection:(id &lt;NSFetchedResultsSectionInfo&gt;)sectionInfo
           atIndex:(NSUInteger)sectionIndex 
     forChangeType:(NSFetchedResultsChangeType)type
{
    switch(type) {
        case NSFetchedResultsChangeInsert:
            [self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] 
                          withRowAnimation:UITableViewRowAnimationFade];
            break;

        case NSFetchedResultsChangeDelete:
            [self.tableView deleteSections:[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 NSFetchedResultsChangeDelete:
            [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] 
                             withRowAnimation:UITableViewRowAnimationFade];
            break;

        case NSFetchedResultsChangeUpdate:
            [self configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
            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];
}

@end</code>

 

Теперь попытаемся разобрать что же мы такого наколбасили. Если вы читали урок CoreData, то практически ничего нового для себя здесь не увидите. Все сводится к созданию запроса и выводу его рузультатов в таблицу. Интересным моментом является способ представления данных выбранных с таблици (записи становятся объектми класса Task, а доступ к их атрибутам осуществляется через точку). Кроме этого следует обратить внимание на получение доступа к managedObjectContext. Инизиализируется он в AppDelegate и там же хранится на него ссылка, а как нам уже известно, AppDelegate является синглтоном, вот к нему мы и обращаемся за получением доступа к managedObjectContext.

 

Пришло время подвести небольшой результат. Сейчас мы имеем таблицу и базу данных записи с базы выводятся в таблицу и у нас есть возможность добавлять эти записи. Осталось реализовать:

 

  • удаление записей с таблицы
  • редактирование записей
  • отбор результатов запроса для каждой таблицы

 

Далее будем реализовывать каждый функционал в этой последовательности. Как мы уже помним с урока Редактирование таблиц, для активации этой возможности требуется метод tableView:commitEditingStyle:forRowAtIndexPath:. Как раз его мы и добавим в наш класс TasksViewController:

 

<code data-result="[object Object]">- (void)tableView:(UITableView *)tableView 
commitEditingStyle:(UITableViewCellEditingStyle)editingStyle 
forRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (editingStyle == UITableViewCellEditingStyleDelete) {
        NSManagedObjectContext *context = [self.taskResults managedObjectContext];
        [context deleteObject:[self.taskResults objectAtIndexPath:indexPath]];

        NSError *error = nil;
        if (![context save:&amp;error]) {
            NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
            abort();
        }
    }    
}</code>

 

В этом методе мы реализовали лишь удаление записи с базы данных, а удаление самой ячейки уже сделано в методах делегата нашего taskResults.

 

Перед тем как перейти к процессу редактирования задач, нам нужно что-то решить с категориями. Ведь в базе нет ни одной записи с ними, а в редакторе нужно как-то их подставлять к задачам. Для этого я предлагаю добавить в AppDelegate метод categories, который будет возвращать список категорий. Но перед этим список нужно заполнить. Посмотрите как я решил эту задачу:

 

<code data-result="[object Object]">#import "AppDelegate.h"
#import "TasksViewController.h"

@interface AppDelegate ()
- (NSFetchedResultsController*)fetchedCategories;
- (void)setDefouldCategories;
@end

@implementation AppDelegate

@synthesize window = _window;
@synthesize managedObjectContext = __managedObjectContext;
@synthesize managedObjectModel = __managedObjectModel;
@synthesize persistentStoreCoordinator = __persistentStoreCoordinator;
@synthesize tabBarController;

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{

    [self setDefouldCategories];

    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];

    //Инициализация вкладок

    self.window.rootViewController = self.tabBarController;
    [self.window makeKeyAndVisible];
    return YES;
}

- (NSFetchedResultsController*)fetchedCategories {
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    fetchRequest.entity = [NSEntityDescription entityForName:@"Category" inManagedObjectContext:self.managedObjectContext];
    [fetchRequest setFetchBatchSize:20];

    NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:YES];
    fetchRequest.sortDescriptors = [NSArray arrayWithObjects:sortDescriptor, nil];

    NSFetchedResultsController *fetchedResults = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest 
                                                                                     managedObjectContext:self.managedObjectContext
                                                                                       sectionNameKeyPath:nil 
                                                                                                cacheName:@"Master"];
    NSError *error = nil;
	if (![fetchedResults performFetch:&amp;error]) {
	    NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
	    abort();
	}

    return fetchedResults;
}

- (void)setDefouldCategories {
    NSFetchedResultsController *fetchedCategories = self.fetchedCategories;
    if (!fetchedCategories.fetchedObjects.count) {
        NSArray *categoryNames = [NSArray arrayWithObjects:@"Personal", @"Business", @"Family", @"Travel", @"Other", nil];

        for (NSString *name in categoryNames) {
            NSEntityDescription *entity = [[fetchedCategories fetchRequest] entity];
            NSManagedObject *category = [NSEntityDescription insertNewObjectForEntityForName:[entity name] 
                                                                      inManagedObjectContext:self.managedObjectContext];
            [category setValue:name forKey:@"name"];
        }
    }
}

- (NSArray*)categories {
    return self.fetchedCategories.fetchedObjects;
}</code>

 

То есть, при запуске приложения мы смотрим на список категорий, если он пуст — добавляем туда пять стандартных записей, а в конце, как я уже говорил, добавлен метод, который возвращает массив категорий.

 

Теперь можна перейти к процессу редактирования задач. Для этой цели я предлагаю создать отдельный класс TaskEditorViewController, добавьте его в проект подобно тому как вы это делали с TasksViewController, толкьо в качестве родителя выберите UIViewController. Посмотрите на предложеную мной реализацию этого класса:

 

TaskEditorViewController.h

<code data-result="[object Object]">#import &lt;UIKit/UIKit.h&gt;
#import "Task.h"

@interface TaskEditorViewController : UIViewController &lt;UITextFieldDelegate&gt;

@property (strong, nonatomic) Task *currentTask;
@property (strong, nonatomic) IBOutlet UITextField *name;
@property (strong, nonatomic) IBOutlet UIButton *dateSelector;
@property (strong, nonatomic) IBOutlet UIButton *categorySelector;
@property (strong, nonatomic) NSDate *selectedDate;
@property (strong, nonatomic) NSManagedObject *selectedCategory;

- (IBAction)selectDate;
- (IBAction)selectCategory;

@end</code>

 

TaskEditorViewController.m

<code data-result="[object Object]">#import "AppDelegate.h"
#import "UNActionPicker.h"

#import "TaskEditorViewController.h"
#import "Task.h"

@interface TaskEditorViewController ()
- (void)updateInterface;
@end

@implementation TaskEditorViewController

@synthesize currentTask;
@synthesize name;
@synthesize dateSelector;
@synthesize categorySelector;
@synthesize selectedDate;
@synthesize selectedCategory;

- (void)viewDidUnload
{
    [super viewDidUnload];
    self.name = nil;
    self.dateSelector = nil;
    self.categorySelector = nil;
}

- (void)viewDidLoad
{
    [super viewDidLoad];

    UIBarButtonItem *save = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemSave 
                                                                          target:self 
                                                                          action:@selector(saveTask)];
    self.navigationItem.rightBarButtonItem = save;

    if (currentTask) {
        name.text = currentTask.name;
        self.selectedDate = currentTask.timeStamp;
        self.selectedCategory = currentTask.category;
    }

    [self updateInterface];
}

- (IBAction)selectDate {
    UNActionPicker *datePicker = [[UNActionPicker alloc] initWithItems:nil 
                                                                 title:@"Select date" 
                                                                  type:DateType];
    [datePicker setActionSheetStyle:UIActionSheetStyleBlackTranslucent];
    [datePicker setCloseButtonTitle:@"Done" color:[UIColor blackColor]];
    datePicker.delegate = self;
    [datePicker showFromTabBar:self.tabBarController.tabBar];
}

- (IBAction)selectCategory {
    AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
    UNActionPicker *datePicker = [[UNActionPicker alloc] initWithItems:appDelegate.categories 
                                                                 title:@"Select Category" 
                                                                  type:SimpleType];
    [datePicker setActionSheetStyle:UIActionSheetStyleBlackTranslucent];
    [datePicker setCloseButtonTitle:@"Done" color:[UIColor blackColor]];
    datePicker.delegate = self;
    [datePicker showFromTabBar:self.tabBarController.tabBar];
}

- (void)didSelectItem:(id)item {
    if ([item isKindOfClass:[NSDate class]]) {
        self.selectedDate = item;
    } else if ([item isKindOfClass:[NSManagedObject class]]) {
        self.selectedCategory = item;
    }

    [self updateInterface];
}

- (BOOL)textFieldShouldReturn:(UITextField *)textField 
{
    [textField resignFirstResponder];
    return YES;
}

- (void)updateInterface {
    if (selectedDate) {
        [dateSelector setTitle:[selectedDate description] forState:UIControlStateNormal];
    }

    if (selectedCategory) {
        [categorySelector setTitle:[selectedCategory valueForKey:@"name"] forState:UIControlStateNormal];
    }
}

- (void)saveTask {
    NSString *alertText;
    if (!name.text.length) {
        alertText = @"Не указано имя задачи";
    } else if (!selectedCategory) {
        alertText = @"Не выбрана категория";
    }

    if (alertText) {
        UIAlertView *warning = [[UIAlertView alloc] initWithTitle:@"Error" 
                                                          message:alertText 
                                                         delegate:nil 
                                                cancelButtonTitle:@"OK" 
                                                otherButtonTitles:nil];
        [warning show];
        return;
    }

    if (!currentTask) {
        AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
        NSManagedObjectContext *context = appDelegate.managedObjectContext;
        NSEntityDescription *entity = [NSEntityDescription entityForName:@"Task" inManagedObjectContext:context];
        self.currentTask = [NSEntityDescription insertNewObjectForEntityForName:[entity name] inManagedObjectContext:context];
    }

    currentTask.name = name.text;
    currentTask.isCompleted = [NSNumber numberWithBool:NO];    
    currentTask.timeStamp = selectedDate? selectedDate:[NSDate date];
    currentTask.category = selectedCategory;

    [self.navigationController popViewControllerAnimated:YES];
}

@end</code>

 

Ничего нового здесь нет. Стандартная работа с объектами интерфейса, а метод создания записи взят с TasksViewController. Стоит лишь отметить, что для выбора даты и категории я использовал свой класс UNActionPicker, о котором писал в Выпадающее меню с пикером (UNActionPicker), только чуток допили его под нужды данного проекта. Кому интересно — можете скачать код в конце статьи и посмотреть на проделаную работу.

 

Вы наверное поняли, что мы уже на завершающей стаднии нашего проекта. Нам соталось лишь связать TasksViewControllerс TaskEditorViewController, для этого изменим в первом метод createTask:

 

<code data-result="[object Object]">- (void)createTask {
    TaskEditorViewController *taskEditor = [[TaskEditorViewController alloc] 
                                            initWithNibName:@"TaskEditorViewController" 
                                            bundle:nil];
    [self.navigationController pushViewController:taskEditor animated:YES];
}</code>

 

И добавим обработку нажатия на ячейку:

 

<code data-result="[object Object]">- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    UIAlertView *question = [[UIAlertView alloc] initWithTitle:@"Task" 
                                                       message:@"Выберите действие" 
                                                      delegate:self
                                             cancelButtonTitle:@"Cancel" 
                                             otherButtonTitles:@"Complete", @"Edit", nil];
    ;
}

- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex
{
    NSIndexPath *selectedIndexPath = [self.tableView indexPathForSelectedRow];
    [self.tableView deselectRowAtIndexPath:selectedIndexPath animated:YES];

    if (buttonIndex == 1) {
        Task *task = [self.taskResults objectAtIndexPath:selectedIndexPath];
        task.isCompleted = [NSNumber numberWithBool:YES];
    } else if (buttonIndex == 2) {
        TaskEditorViewController *taskEditor = [[TaskEditorViewController alloc] 
                                                initWithNibName:@"TaskEditorViewController" 
                                                bundle:nil];
        taskEditor.currentTask = [self.taskResults objectAtIndexPath:selectedIndexPath];
        [self.navigationController pushViewController:taskEditor animated:YES];

    }
}</code>

 

О нажатии по-подробнее. В методе таблицы didSelectRowAtIndexPath мы не снимаем выделение с ячейки, а отображаем сообщение. В методе делагата сообщения (при выборе какой-то кнопки), узнаем индекс выбранной ячейки и по нему получаем такущую задачу. А затем решаем что с этой задачей делать.

 

Вот только фильтр задач у нас еще не установлен. То есть, во всех вкладках у нас будет отображаться одинаковая информация. Для добавления условия в результат выборки с таблицы в CoreData используется такой же механизм, как для отбора в массивах. Мы уже сталкивались с ним в уроке NSArray (массивы). То есть, это предикаты (NSPredicate). Давайте посмотрим как можно их применить в нашем случае. В TasksViewController я добавил еще один метод filterRequest, который возвращает правильно сформированый предикат. И соответственно, изменил метод создания запроса:

 

<code data-result="[object Object]">- (NSPredicate*)filterRequest {
    if (![self.navigationItem.title isEqualToString:@"All"]) {
        return [NSPredicate predicateWithFormat:@"isCompleted==%i",
                [self.navigationItem.title isEqualToString:@"Completed"]];
    } else {
        return nil;
    }
}</code>

 

<code data-result="[object Object]">- (void)createFetchRequest {
    AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
    NSManagedObjectContext *managedObjectContext = appDelegate.managedObjectContext;

    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    fetchRequest.entity = [NSEntityDescription entityForName:@"Task" 
                                      inManagedObjectContext:managedObjectContext];

    NSPredicate *filterRequest = self.filterRequest;
    if (filterRequest) {
        fetchRequest.predicate = filterRequest;
    }

    NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"timeStamp" ascending:NO];
    fetchRequest.sortDescriptors = [NSArray arrayWithObjects:sortDescriptor, nil];

    self.taskResults = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest 
                                                           managedObjectContext:managedObjectContext
                                                             sectionNameKeyPath:nil 
                                                                      cacheName:@"Master"];
    NSError *error = nil;
	if (![self.taskResults performFetch:&amp;error]) {
	    NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
	    abort();
	}
    self.taskResults.delegate = self;
}</code>

 

Теперь можете смело запускать проект и пользоваться своим личным менеджером задач.

 

Хочу добавить несколько слов к этому уроку. Для работы с базой данных мы использовали класс AppDelegate, что по правилам MVC не очень правильно. Нам следовало бы создать отдельный класс (какой-то синглтон), в котором были бы все методы по работе с базой данных. Второй момент — ячейки, их работа с алертом больше похожа на подпорку (хотя реализовывает нужный нам функционал). Разумнее было бы сделать Свои ячейки (custom cell)l с кнопокой для редактирования. Но это темы уже других уроков.

 

 

Comments are closed.