вторник, 27 октября 2015 г.

36 UIPopoverPresentationController, NSUserDefaults, UIDatePicker

Видео урок  36 посвящен описанию класса UIPopoverController.
Popover представляет собой всплывающее модальное окно со стрелочкой - якорем указывающим на элемент к которому относится Popover.
 В новой версии xCode класс UIPopoverController уже deprecated (в IOS 9.0).
Поэтому подробно описывать урок смысла нет (т.к. UIPopoverController использовать уже не нельзя).
В сообщении об ошибке говорится, что теперь popover включен в презентации UIViewController. Используйте модальный стиль презентаций  UIModalPresentationPopover и UIPopoverPresentationController.
Попробую домашнее задание реализовать с учетом нововведений в  Xcode.

Нашел вот такой пример создания Popover в коде:

Declare a property of UIPopoverPresentationController:
@property(nonatomic,retain)UIPopoverPresentationController *dateTimePopover8;
Use the following method to present the popover from UIButton:
- (IBAction)btnSelectDatePressed:(id)sender
{
    UINavigationController *destNav = [[UINavigationController alloc] initWithRootViewController:dateVC];/*Here dateVC is controller you want to show in popover*/
    dateVC.preferredContentSize = CGSizeMake(280,200);
    destNav.modalPresentationStyle = UIModalPresentationPopover;
    _dateTimePopover8 = destNav.popoverPresentationController;
    _dateTimePopover8.delegate = self;
    _dateTimePopover8.sourceView = self.view;
    _dateTimePopover8.sourceRect = sender.frame;
    destNav.navigationBarHidden = YES;
    [self presentViewController:destNav animated:YES completion:nil];
}

Use the following method to present the popover from UIBarButtonItem:

- (IBAction)btnSelectDatePressed:(id)sender
{
    UINavigationController *destNav = [[UINavigationController alloc] initWithRootViewController:dateVC];/*Here dateVC is controller you want to show in popover*/
    dateVC.preferredContentSize = CGSizeMake(280,200);
    destNav.modalPresentationStyle = UIModalPresentationPopover;
    _dateTimePopover8 = destNav.popoverPresentationController;
    _dateTimePopover8.delegate = self;
    _dateTimePopover8.sourceView = self.view;
     CGRect frame = [[sender valueForKey:@"view"] frame];
    frame.origin.y = frame.origin.y+20;
    _dateTimePopover8.sourceRect = frame;
    destNav.navigationBarHidden = YES;
    [self presentViewController:destNav animated:YES completion:nil];
}

Implement this delegate method too in your view controller:

- (UIModalPresentationStyle) adaptivePresentationStyleForPresentationController: (UIPresentationController * ) controller {
    return UIModalPresentationNone;
}
To dismiss this popover, simply dismiss the view controller. Below is the code to dismiss the view controller:
-(void)hideIOS8PopOver
{
    [self dismissViewControllerAnimated:YES completion:nil];
}

В процессе выполнения домашнего задания разобрался с разными моментами создания Popover (т.к. методы указанные в уроке уже устарели).

Popover легко можно создать в StoryBoard. Создаем UIButton или  UIBarButtonItem, контроллер который будет отображаться как Popover.
От кнопки тянем к новому контроллеру Segue и выбираем PopoverPresentation.
В принципе на Ipad уже будет все работать.
Чтобы и на айфоне нормально выводился поповер, необходимо добавить код:

-(void) prepareForSegue:(nonnull UIStoryboardSegue *)segue sender:(nullable id)sender{
    if ([segue.identifier isEqualToString:@"showView"]) {
        
        UINavigationController * nvc = segue.destinationViewController;
     
        UIPresentationController * pc = nvc.presentationController;
        pc.delegate=self;
    }
}

Так же обязательно присутствие метода для всех типов создания поперев (из кода или storyboard)

- (UIModalPresentationStyle) adaptivePresentationStyleForPresentationController: (UIPresentationController * ) controller {
    return UIModalPresentationNone;
}

Если segue начинается не с кнопки, а просто связывает два контроллера, то можно в action кнопки указать использовать нужный segue:

- (IBAction)actionButton:(UIButton *)sender {

    [self performSegueWithIdentifier:@"showView" sender:self];
}

Так же нужно прописать, что наш класс поддерживает протокол

@interface ViewController ()<UIPopoverPresentationControllerDelegate>

Для создания Popover из кода:
Создаем property
@property(nonatomic,retain)UIPopoverPresentationController *popoverPresentation;

В обработчик кнопки добавляем код

    TAPopoverController *dateVC=[[TAPopoverController alloc]init];
// это контроллер который будут отображен в Popover окне
    
    UINavigationController *destNav = [[UINavigationController alloc] initWithRootViewController:dateVC];/*Here dateVC is controller you want to show in popover*/
    dateVC.preferredContentSize = CGSizeMake(150,150);
    destNav.modalPresentationStyle = UIModalPresentationPopover;
    self.popoverPresentation = destNav.popoverPresentationController;
    self.popoverPresentation.delegate = self;
    self.popoverPresentation.sourceView = self.view;
    CGRect frame = [[sender valueForKey:@"view"] frame];
    frame.origin.y = frame.origin.y+20;
    self.popoverPresentation.sourceRect = frame;
    destNav.navigationBarHidden = YES;
    [self presentViewController:destNav animated:YES completion:nil];

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





Объявляем протокол:

@protocol DataPopoverDelegate <NSObject>
-(void) dataFromPopover:(NSDate*) date;
-(void) currentEducationSelected:(NSInteger) currentEducation;
@end

Родительский контроллер поддерживает этот протокол и реализует методы этого протокола 

@interface ViewController : UITableViewController <UIAdaptivePresentationControllerDelegate,DataPopoverDelegate>

В дочернем контроллере объявляем член класса - делегат 

@interface TADatePickerController : UIViewController
@property (weak, nonatomic) IBOutlet UIDatePicker *birthdayDataPicker;
@property (strong,nonatomic) NSDate *dateBirthday;
@property(weak,nonatomic) id<DataPopoverDelegate> delegate;
@end


Реализация метедов протокола в родительском классе

-(void) dataFromPopover:(NSDate*) date{
    self.dateBirthday=date;
    [self displayBirthday];
}
-(void) currentEducationSelected:(NSInteger) currentEducation{
    self.educationTextField.text=educationArray[currentEducation];
    self.currentEducationSelected=currentEducation;
}

При изменении данных в дочернем классе вызываем метод делегата

- (void)tableView:(UITableView *)tableView didHighlightRowAtIndexPath:(NSIndexPath *)indexPath {
    self.currentSelection=indexPath.row;
    [tableView reloadData];
    [self.delegate  currentEducationSelected:self.currentSelection];
}

Так же в этом домашнем задании реализовано сохранения и загрузка данных через NSUserDefaults


- (IBAction)actionSaveData:(UIBarButtonItem *)sender {
    NSUserDefaults *userDefault=[NSUserDefaults standardUserDefaults];
    [userDefault setObject:self.firstNameTextField.text forKey:kSettingsFirstName];
    [userDefault setObject:self.lastNameTextField.text forKey:kSettingsLastName];
    [userDefault setInteger:self.sexSegmentControl.selectedSegmentIndex forKey:kSettingsSex];
    [userDefault setObject:self.dateBirthday forKey:kSettingsBirthDay];
    [userDefault setInteger:self.currentEducationSelected forKey:kSettingsEducation];
    [userDefault synchronize];

}


- (IBAction)actionLoadData:(id)sender {
    NSUserDefaults *userDefault=[NSUserDefaults standardUserDefaults];
    self.firstNameTextField.text=[userDefault objectForKey:kSettingsFirstName];
    self.lastNameTextField.text=[userDefault objectForKey:kSettingsLastName];
    self.sexSegmentControl.selectedSegmentIndex=[userDefault integerForKey:kSettingsSex];
    self.dateBirthday=[userDefault objectForKey:kSettingsBirthDay];
    [self displayBirthday];
    self.currentEducationSelected=[userDefault integerForKey:kSettingsEducation];
    self.educationTextField.text=educationArray[self.currentEducationSelected];
}

Подробное описание   модального диалога Alert - UIAlertcontroller
Домашнее задание

Ученик

1. Создайте универсальное приложение (айпад / айфон)
2. Первый контроллер должен быть статической таблицей с навигейшн баром
3. В правом углу на навигейшине должна быть кнопка инфо, если на нее нажать, то вылазит поповер с объяснением, что это такое за приложение :)

Студент

4. В таблице создайте классические ячейки:
имя + текстфилд
фамилия + текстфилд
пол + сегментед контрол (мужской/женский)
Дата рождения + текстфилд
Образование + текстфилд

5. с первыми тремя ячейками все понятно, а вот дальше самое интересное

Мастер

6. При нажатии на текст филд с датой рождения текст филду должно быть запрещено входить в режим редактирования, а вместо этого из него должен появиться поповер с UIDatePicker. При изменении даты, содержимое текст филда должно меняться (то есть мы не мучаем юзера форматами ввода, мы просто даем ему барабан с датами и предлагаем выбрать самому)

7. Подсказка. Вам надо сделать контроллер с дейт пикером, а дейт пикер это наследник от UIControl, то есть у него есть акшин valueChanged или типо того. У контроллера нужно создать проперти делегат, по которому мы будем отправлять данные, полученные с барабана. То есть по простому: контроллер следит за барабаном и отправляет изменения своему делегату. Не забудьте установить делегат перед создания поповера.

Супермен

8. Тоже самое сделать с образованием. Образование это список типа, неполное среднее, среднее, неполное высшее, высшее и тд, то есть если делать в сегментед контролах, то не поместится.

9. Когда нажимаем на образование, появляется поповер с контроллером и таблицей. Причем, выбранное образование должно быть отмечено чекбоксом. (кстати выбранная дата рождения в мастере тоже должна стоять по умолчанию в новом поповере)

10. У этого контроллера тоже должен быть делегат. По нажатию на ячейку вы должны изменить текущий выбор на новый (поменять чекбокс) и передать сообщение делегату, после чего тот должен изменить содержимое текст филда

Выполнены все задания к уроку 36 Popover

Исходник  проекта

Комментариев нет:

Отправить комментарий