Главная
Блог разработчиков phpBB
 
+ 17 предустановленных модов
+ SEO-оптимизация форума
+ авторизация через соц. сети
+ защита от спама

Core Data для iOS. Глава №3. Теоретическая часть

Anna | 2.07.2014 | нет комментариев

Сегодня хочу начать написание ряда лекций с фактическими заданиями по книге Михаеля Привата и Роберта Варнера «Pro Core Data for iOS», которую можете приобрести по этой ссылке. Всякая глава будет содержать теоретическую и утилитарную часть.

Оглавление:

Введение

В этой главе мы сотворим приложение с двумя таблицами и «один-ко-многим» отношением между ними. Представьте, что вы вызвались добровольцем следить за командами из младшей футбольной лиги и их составом. В нашей модели данных будет две таблицы: Team (хранит информацию о названии команды и цвете их формы) и Player (хранит информацию об игроке команды — почтовый ящик, имя и фамилию). ?сно, что у одной команды уйма игроков, а игрок принадлежит одной команде.
Сделаем мы это приложение сперва с применением SQLite в качестве хранилища, а потом испробуем применять другие варианты (хранение в памяти и произвольные хранилища (atomic store)).

Интерфейс

Наше приложение назовём League Manager и состоять оно будет из 4 экранов:

  • Список команд
  • Добавить/редактировать команду
  • Список игроков
  • Добавить /редактировать игрока

Экран со списком команд отображает сохраненные локально футбольные команды с цветами их формы. На экране присутствует кнопка для добавления новой команды и кнопка Edit для удаления. Вот как выглядит экран:
image

Экран добавления/редактирования футбольной команды состоит из 2-х полей для ввода названия команды и цвета их формы. Данный экран отображается позже нажатия на кнопку на экране «Список команд» для добавления новой команды, либо при нажатии на одну из команд из списка для её редактирования.
image

Экран со списком игроков отображает всех игроков определенной команды. Переход на данный экран осуществляется позже нажатия на синюю стрелочку на экране «Список команд» наоборот всякой команды:
image

Как и с командами, при нажатии на возникает экран добавления нового игрока в команду, а при нажатии наEdit дозволено редактировать информацию об игроке.
image

Сейчас, когда мы знаем что нам предстоит делать, можем приступать.

Применение SQLite в качестве хранилища

Запускаем ХКод и создаем новейший план.
image

Называем его LeagueManager и в качестве идентифи приложения из образца Master-Details View Application создается еще один контроллер с наименованием DetailViewController. Вы можете применять данный класс, но так как нам нужно отображать информацию о команде и игроках, то отменнее избавиться от этого контроллера и создавать новые контроллеры с соответствующими именами.
Удалите строку #import DetailViewController.h из MasterViewController.m. Обнаружьте способtableView:didSelectRowAtIndexPath: и очистите его тело. Выглядит дальнейшим образом:

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

Позже чего мы можем спокойно удалять все три файла DetailViewController (.h, .m, .xib). Если мы запустим приложение и нажмем на кнопку , то приложение аварийно прекращает свою работу. Данный по бывшему привязан к способу insertNewObject:, тот, что мы удалили. Нам нужно привязать к этой кнопке вероятность создания новой команды, а вернее — показать модальное окно с полями для ввода информации (название команды и цвет формы) о новой команде. Это же окно будет применяться для редактирования теснее имеющихся команд при нажатии на ячейку с командой на экране списка команд.
Создаем новейший класс, родителем нашего класса будет являться UIViewController:
image
Назовем данный класс TeamViewController и не забываем установить галочку на опции With XIB for user interface.
image

Открываем TeamViewController.h. В League Manager, класс MasterViewController управляетNSManagedObjectContext, значит в TeamViewController будет нужна ссылка на эту среду управления объектами и соответствующий способ инициализации. Так как данный контроллер будет отвечать и за редактирование информации о команде, то при инициализации стоит передавать объект команды (для этого нам так же необходимо будет сделать качество и добавить в способ инициализации). Пользовательский интерфейс экрана добавления/редактирования команды будет содержать два текстовых поля — для названия команды и цвета их формы, для них нужно сделать соответствующие свойства в TeamViewController. На данном экране так же будут находиться две кнопки — кнопка сохранения (Save) новой команды и кнопка отмены (Cancel). В TeamViewController обязаны быть способы-обработчики нажатий на эти кнопки.

TeamViewController.h

#import <UIKit/UIKit.h>

@class MasterViewController;

@interface TeamViewController : UIViewController {
   IBOutlet UITextField *name;
   IBOutlet UITextField *uniformColor;
   NSManagedObject *team;
   MasterVIewController *masterController;
}

@property (nonatomic, retain) UITextField *name;
@property (nonatomic, retain) UITextField *uniformColor;
@property (nonatomic, retain) NSManagedObject *team;
@property (nonatomic, retain) MasterViewController *masterController;

- (IBAction)save:(id)sender;
- (IBAction)cancel:(id)sender;
- (id)initWithMasterController:(MasterViewController *)aMasterController team:(NSManagedObject *)aTeam;

@end

Открываем TeamViewController.m, импортируем MasterViewController.h, удаляем способ initWithNibName:, добавляем @synthesize для nameteam и masterController. Добавляем вот такой способ инициализации:

- (id)initWithMasterController:(MasterController *)aMasterController team:(NSManagedObject *)aTeam {
   if((self = [super init])){
      self.masterController = aMasterController;
      self.team = aTeam;
   }

   return self;
}

В случае, если пользователь приложения захочет сделать новую команду, то параметр aTeam будет равен nil, и контроллер TeamViewController.m будет отвечать за создание нового NSManagedObject объекта. В случае же, если пользователь предпочтет одну из существующих команд для редактирования, то за контроллером остаётся ответственность заполнить текстовые поля на экране соответствующими данными из командного объекта (имя команды и цвет формы). Конечный функционал мы добавим в способ viewDidLoad:

- (void)viewDidLoad{
   [super viewDidLoad];

   if(team != nil){
      name.text = [team valueForKey:@"name"];
      uniformColor.text = [team valueForKey:@"uniformColor"];
   }
}

Сейчас осталось реализовать обработчики событий при нажатии на кнопки Save и Cancel:

- (IBAction)save:(id)sender
{
   if(masterController != nil){
      if(team != nil){
         [team setValue:name.text forKey:@"name"];
         [team setValue:uniformColor.text forKey:@"uniformColor"];
         [masterController saveContext];
      } else {
         [masterController insertNewTeamWithName:name.text uniformColor:uniformColor.text];
      }
   }

   [self dismissModalViewControllerAnimated:YES];
}

Способ cancel: легко убирает окно редактирования/добавления команды.
TeamViewController.m

#import "TeamViewController.h"
#import "MasterViewController.h"

@implementation TeamViewController

@synthesize name;
@synthesize uniformColor;
@synthesize team;
@synthesize masterController;

- (id)initWithMasterController:(MasterController *)aMasterController team:(NSManagedObejct *)aTeam {
   if((self = [super init])){
      self.masterController = aMasterController;
      self.team = aTeam;
   }

   return self;
}

- (void)didReceiveMemoryWarning{
   [super didReceiveMemoryWarning];
}

#pragma mark - View lifecycle

- (void)viewDidLoad{
   [super viewDidLoad];
   if(team != nil){
      name.text = [team valueForKey:@"name"];
      uniformColor.text = [team valueForKey:@"uniformColor"];
   }
}

- (void)viewDidUnload{
   [super viewDidUnload];
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
   return (interfaceOrientation == UIInterfaceOrientationPortrait);
}

#pragma mark - Button handlers

- (IBAction)save:(id)sender{
   if(masterController != nil){
      if(team != nil){
         [team setValue:name.text forKey:@"name"];
         [team setValue:uniformColor.text forKey:@"uniformColor"];
         [masterController saveContext];
      } else {
         [masterController insertTeamWithName:name.text uniformColor:uniformColor.text];
      }
   }

   [self dismissModalViewControllerAnimated:YES];
}

- (IBAction)cancel:(id)sender{
   [self dismissModalViewControllerAnimated:YES];
}

@end

Позже того, как написан код для взаимодействия с элементами пользовательского интерфейса, мы можем приступить собственно к созданию этого самого пользовательского интерфейса. ОткрываемTeamViewController.xib, он у нас первоначально пустой должен быть. Устанавливаем туда две надписи, два поля ввода текста и две кнопк, объединяем действия кнопок с соответствующими способами-обработчиками. Итоговый вид приблизительно такой:
image

Перед тем, как запускать приложение на выполнение, возвратимся в MasterViewController и добавим код для отображения экрана с информацией о команде. Отображать экран редактирования команды мы обязаны в 2-х случаях: 1) пользователь нажал на 2) пользователь нажал на команду из списка. Начнем с нажатия на кнопку . Объявите новейший способ в MasterViewController.h:

- (void)showTeamView;

Идем в MasterViewController.m, импортируем TeamViewController.h и реализуем указанный выше способ дальнейшим образом:

- (void)showTeamView{
   TeamViewController *team = [[TeamViewController alloc] initWithMasterController:self team:nil];
   [self presentModalViewController:teamViewController animated:YES];
}

Сейчас осталось «связать» нажатие на с действием. Переходим в viewDidLoad способ и заменяем вот данный код:

UIBarButtonItem *addButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(insertNewObject)];

на данный:

UIBarButtonItem *addButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(showTeamView)];

На данном этапе приложение теснее может создавать команды, но перед тем, как запустить приложение на выполнение предлагаю добавить сразу код для редактирования команд:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath)indexPath{
   NSManagedObject *team = [[self fetchedResultsController] objectAtIndexPath:indexPath];
   TeamViewController *teamViewController = [[TeamViewController alloc] initWithMasterController:self team:team];

   [self presentModalViewController:teamViewController animated:YES];
}

Запустите приложение на выполнение:
image

Сейчас мы можем добавлять команды, редактировать их. Добавьте несколько команд, в списке они будут отсортированы в лексикографическом порядке.
image
image

Дозволено подметить, что мы можем создавать команды с пустыми именами либо цветами форм. Если бы это было настоящее приложение, то нам нужно было принять некоторые обеспечительные меры от дрянных пользовательских данных. Сходственный «промах» с проверкой данных был нами специально оставлен, потому как в Главе №5 мы изучим методы валидации данных.
Закройте приложение, запустите снова… наслаждайтесь вашими трудами… но, мы завершили только работу с командами, а с игроками еще предстоит поработать. Этим мы и займемся в дальнейшем разделе.

Пользовательский интерфейс для управления игроками

Для реализации пользовательского интерфейса управления игроками нам потребуется два экрана и соответствующих контроллера: один для отображения списка игроков в команде, а 2-й для добавления нового игрока либо редактирования теснее присутствующего. Эти контроллеры в огромнее части являются зеркальными копиями контроллеров для работы с командами, правда они и не содержатNSFetchedResultsController и остальной код для работы с Core Data, взамен этого они делегируют взаимодействие с Core Data MasterViewController.

Сотворим сперва контроллер и экран для отображения списка игроков команды. Создаем новейший контроллер PlayerListViewController, устанавливаем его родительский класс UITableViewController и снимаем галочку с опции «With XIB for user interface». Открываем файл PlayerListViewController.h. Данный класс отвечает за отображение списка игроков команды, а значит в этом классе нужна ссылка на объект команды. Так же, с учетом того, что данный класс делегирует взаимодействие с Core Data контроллеруMasterViewController, то нужна еще и ссылка на сам контроллер.
На экране будет кнопка для добавления нового игрока. Объявим соответствующий способ-обработчик нажатия.

PlayerListViewController.h

#import <UIKit/UIKit.h>

@class MasterViewController;

@interface PlayerListViewController : UITableVIewController {
   NSManagedObject *team;
   MasterViewController *masterViewController;
}

@property (nonatomic, retain) NSManagedObject *team;
@property (nonatomic, retain) MasterViewController *masterController;

- (id)initWithMasterController:(MasterViewController *)aMasterController team:(NSManagedObject *)aTeam;
- (void)showPlayerView;
- (NSArray *)sortPlayers;

@end

Откройте файл PlayerListViewController.m и импортируйте MasterVIewController.h, синтезируйте свойства team и masterController. Измените сгенерированный способ initWithStyle: наinitWithMasterController:, тот, что принимает два свойства и сберегает дальнейшим образом:

- (id)initWithMasterController:(MasterVIewController *)aMasterVIewController team:(NSManagedObject *)aTeam {
   if((self = [super init])){
      self.masterController = aMasterController;
      self.team = aTeam;
   }
   return self;
}

Сгенерированный механически способ viewDidLoad изменим дальнейшим образом:

- (void)viewDidLoad{
   [super viewDidLoad];

   self.title = @"Player";

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

А способ showPlayerView пока оставим пустым:

- (void)showPlayerView{
}

Подкорректируем способ viewWillAppear: дальнейшим образом:

- (void)viewWillAppear:(BOOL)animated{
   [super viewWillAppear:animated];
   [self.tableView reloadData];
}

Список игроков в таблице будет отсортирован в алфавитном порядке в исключительной сегменты (разделе). Для того, Дабы получить список всех игроков команды нужно вызвать способ valueForKey:@"players"объекта команды, тот, что вернет нам NSSet* игроков. Ниже представлен код для настройки отображения таблицы:

- (NSUInteger)numberOfSectionInTableView:(UITableView *)tableView {
   return 1;
}

- (NSInteger)tableView:(UITableVIew *)tableView numberOfRowsInSection:(NSInteger)section
{
   return [(NSSet *)[team valueForKey:@"players"] count];
}

- (UITableViewCell *)tableView:(UITableVIew *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
   static NSString *CellIdentifier = @"PlayerCell";

   UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(cell == nil){
      cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier];
   }

   NSManagedObject *player = [[self sortPlayers] objectAtIndex:indexPath.row];
   cell.textLabel.text = [NSString stringWithFormat:@"%@ %@", [[player valueForKey:@"firstName"] description], [[player valueForKey:@"lastName"] description]];
   cell.detailTextLabel.text = [[player valueForKey:@"email"] description];

   return cell;
}

Выше есть вызов способа sortPlayers, тот, что возвращает отсортированный массив игроков:

- (NSArray *)sortPlayers{
   NSSortDescriptor *sortLastNameDescriptor = [[NSSortDescriptor alloc] initWithKey:@"lastName" ascending:YES];
   NSArray *sortDescriptors = [NSArray arrayWithObjects:sortLastNameDescriptor, nil];
   return [[(NSSet *)[team valueForKey:@"players"] allObjects] sortedArrayUsingDescriptors:sortDescriptors];
}

Для того, Дабы отображать список игроков команды возвратимся в MasterViewController.m и добавим способ, тот, что будет обрабатывать нажатие на добавочный аксессуар (синий элемент ячейки):

- (void)tableView:(UITableVIew *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath{
   NSManagedObject *team = [self.fetchedResultsController objectAtIndexPath:indexPath];
   PlayerListViewController *playerListViewController = [[PlayerListViewController alloc] initWithMasterController:self team:team];
   [self.navigationController pushViewController:playerListViewController animated:YES];
}

Добавьте импорт PlayerListViewController.h в MasterViewController.m. Сейчас соберем и запустим приложение. В списке команд мы видим ранее сделанные нами команды, при нажатии на аксессуар-кнопку открывается список игроков выбранной команды (пока списки игроков пусты, потому как мы не реализовали добавление нового игрока).
image

Добавление, редактирование и удаление игроков

Приложение теснее примерно готово; исключительное, что стоит реализовать, так это добавление/редактирование/удаление игроков.
Сотворим новейший контроллер (UIViewController) c соответствующим XIBом и назовем егоPlayerViewController. Он будет схож на TeamViewController, но содержать три поля: lastNamefirstName иemail. Контроллер так же содержит ссылку на MasterViewController для того, Дабы иметь вероятность применять написанные ранее способы для работы с Core Data. Так же будут присутствовать еще два свойства: команды в которой играет игрок и сам игрок. Если объект игрока равен nil, тоPlayerViewController знает, что нужно сделать нового игрока, в отвратном случае — редактирование данных игрока. На экране у нас будет три кнопки: сберечь, отмена и удалить. При запросе на удаление игрока мы будем запрашивать доказательство у пользователя в виде отображения UIActionSheeta, следственно нужно, Дабы PlayerViewController реализовывал способы протокола UIActionSheetDelegate.

PlayerViewController.h

#import <UIKit/UIKit.h>

@class MasterViewController;

@interface PlayerViewController : UIViewController <UIActionSheetDelegate> {
   IBOutlet UITextField *firstName;
   IBOutlet UITextField *lastName;
   IBOutlet UITextField *email;

   NSManagedObject *team;
   NSManagedObject *player;

   MasterViewController *masterViewController;
}

@property (nonatomic, retain) UITextField *firstName;
@property (nonatomic, retain) UITextField *lastName;
@property (nonatomic, retain) UITextField *email;
@property (nonatomic, retain) NSManagedObject *team;
@property (nonatomic, retain) NSManagedObject *player;
@property (nonatomic, retain) MasterViewController *masterController;

- (IBAction)save:(id)sender;
- (IBAction)cancel:(id)sender;
- (IBAction)confirmDelete:(id)sender;
- (id)initWithMasterController:(MasterViewController *)aMasterController team:(NSManagedObject *)aTeam player:(NSManagedObject *)aPlayer;

@end

Сейчас откройте PlayerViewController.m, импортируйте MasterViewController.h и добавьте @synthesizeдля всех свойств из интерфейса. Добавьте способ инициализации в PlayerVIewController.m, тот, что будет получать экземпляр класса MasterViewController, команду и допустимо объект игрока.

- (id)initWithMasterController:(MasterViewController *)aMasterController team:(NSManagedObject *)aTeam player:(NSManagedObject *)aPlayer{
   if((self = [super init])){
      self.masterController = aMasterController;
      self.team = team;
      self.player = player;
   }

   return self;
}

В способ viewDidLoad добавим код для заполнения текстовых полей данными игрока, если он не равен nil.

- (void)viewDidLoad {
   [super viewDidLoad];
   if(player != nil){
      firstName.text = [player valueForKey:@"firstName"];
      lastName.text = [player valueForKey:@"lastName"];
      email.text = [player valueForKey:@"email"];
   }
}

Дальнейшим нашим шагом будет реализация способов-обработчиков нажатий на кнопки:

- (IBAction)save:(id)sender{
   if(masterController != nil){
      if(player != nil){
         [player setValue:firstName.text forKey:@"firstName"];
         [player setValue:lastName.text forKey:@"lastName"];
         [player setValue:email.text forKey:@"email"];
      } else {
         [masterController insertPlayerWithTeam:team firstName:firstName.text lastName:lastName.text email:email.text];
      }
   }

   [self dismissModelViewControllerAnimated:YES];
}

- (IBAction)cancel:(id)sender{
   [self dismissModalViewControllerAnimated:YES];
}

Способа insertPlayerWithTeam:firstName:lastName:email: в контроллере MasterViewController пока нет, но мы его напишем дословно через пару минут.  Сперва реализуем confirmDelete: способ, тот, что вызывается при нажатии на кнопку «Delete»(Удалить). Данный способ не будет сразу удалять игрока, а он запросит у пользователя подтверждения на выполнение данного действия (делается для того, Дабы избежать случайных нажатий и удалений игроков). Вот как будет выглядеть способ confirmDelete::

- (IBAction)confirmDelete:(id)sender{
   if(player != nil){
      UIActionSheet *confirm = [[UIActionSheet alloc] initWithTitle:nil delegate:self cancelButtonTitle:@"Cancel" destructiveButtonTitle:@"Delete Player" otherButtonTitles:nil];
      confirm.actionSheetStyle = UIActionSheetStyleBlackTranslucent;
      [confirm showInView:self.view];
   }
}

Делегатом, тот, что будет обрабатывать действия идеальные в UIActionSheetе будет нынешний класс. При нажатии на кнопки UIActionSheetа будет вызываться способ clickedButtonAtIndex:, а значит нужно его реализовать. В способе будет проверка на то какая кнопка была нажата и, если кнопка Delete, то будет вызван способ (тот, что мы так же потом реализуем) удаления игрока:

- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex{
   if(buttonIndex == 0 && masterController != nil){
      [masterController deletePlayer:player];
      [self dismissModalViewControllerAnimated:YES];
   }
}

Возвратимся сейчас к MasterViewController.h и объявим два способа, которые мы еще не реализовывали, но теснее применяли:

- (void)insertPlayerWithTeam:(NSManagedObject *)team firstName:(NSString *)firstName lastName:(NSString *)lastName email:(NSString *)email;
- (void)deletePlayer:(NSManagedObject *)player;

Открываем сейчас MasterViewController.m и реализуем способы:

- (void)insertPlayerWithTeam:(NSManagedObject *)team firstName:(NSString *)firstName lastName:(NSString *)lastName email:(NSString *)email{
   NSManagedObjectContext *context = [self.fetchedResultsController managedObjectContext];
   NSManagedObject *player = [NSEntityDescription insertNewObjectForEntityForName:@"Player" inManagedObjectContext:context];

   [player setValue:firstName forKey:@"firstName"];
   [player setValue:lastName forKey:@"lastName"];
   [player setValue:email forKey:@"email"];
   [player setValue:team forKey:@"team"];

   [self saveContext];
}

- (void)deletePlayer:(NSManagedObject *)player{
   NSManagedObjectContext *context = [self.fetchedResultsController managedObjectContext];
   [context deleteObject:player];
   [self saveContext];
}

Последним шагом является создание пользовательского экрана для добавления/редактирования игрока. Выберите PlayerViewController.xib, приведите его к такому виду, как на картинке ниже и объедините все Action с соответствующими кнопками.
image

Для того, Дабы отобразить данный экран нужно возвратиться к реализации способа showPlayerView:, тот, что мы ранее применяли. Импортируйте в PlayerListViewController.m файл PlayerViewController.h.

- (void)showPlayerVIew{
   PlayerVIewController *playerViewController = [[PlayerVIewController alloc] initWithMasterController:masterController team:team player:nil];
   [self presentModalViewController:playerVIewController animated:YES];
}

Нам также нужно обрабатывать нажатия на ячейки таблицы списка игроков. Находим вPlayerListViewController.m механически сгенерированный способ didSelectRowAtIndexPath: и приводим его к дальнейшему виду:

- (void)tableView:(UITableVIew *)tableVIew didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
   NSManagedObject *player = [[self sortPlayers] objectAtIndex:indexPath.row];
   PlayerViewController *playerViewController = [[PlayerVIewController alloc] initWithMasterController:masterController team:team player:player];
   [self presentModalVIewController:playerViewController animated:YES];
}

На этоv реализация приложения управления командами и игроками закончена. Запустите приложение. Добавьте игроков, удалите игроков, удалите команды с игроками.

Оставшиеся сегменты для перевода (cкоро будут, глава как видите огромная и, сразу всё перевести довольно сложно и изнурительно):

Проверка данных хранилища
Применение хранилища данных в памяти (In-memory persistent store)
Разработка собственного типа хранилища
Инициализация настраиваемого хранилища
Осуществление маппинга между NSManagedObject и NSAtomicStoreCacheNode
Сериализация данных
Применение настраиваемого (кастомного) хранилища
Что насчет XML-типа хранилища?

Источник: programmingmaster.ru

Оставить комментарий
Форум phpBB, русская поддержка форума phpBB
Рейтинг@Mail.ru 2008 - 2017 © BB3x.ru - русская поддержка форума phpBB