понедельник, 19 октября 2015 г.

34. StoryBoard: UINavigationController, UITableViewController

Видео урок находится здесь.
В этом уроке рассказывается как переделать проект файлового менеджера из прошлого урока с помощью StoryBoard.
Первым делом удаляем наш код создания контроллеров из функции didFinishLaunchingWithOptions   класса AppDelegate.
Теперь это будет делаться из StoryBoard.
Если файла StoryBoard не существует, если существует переходим к следующему шагу.
Создаем в storyboard UINavigationController и UITableViewController.
Перетягиваем с одного на другой линию и устанавливаем root контроллер.

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

-(void) setPath:(NSString *)path{
    _path=path;
    NSError *error=nil;
    self.contents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:path error:&error];
    if (error){
        NSLog(@"%@",[error localizedDescription]);
    }

    [self.tableView reloadData];
    self.navigationItem.title=[self.path lastPathComponent];
}
-(id) initWithFolder: (NSString *)path{
    self = [super initWithStyle:UITableViewStyleGrouped];
    if(self){
        self.path=path;
        }
    return self;
}

Все работает как в предыдущем проекте за исключением кнопки возврата в Root контроллер.

Это происходит так как метод viewDidLoad в котором происходит создание кнопки вызывается еще до того, как контроллер создается.
Если перенести код создания кнопки в функцию viewWillAppear, то все работает нормально.
-(void) viewDidAppear:(BOOL)animated{

    [super viewDidAppear:animated];
    self.navigationItem.title=[self.path lastPathComponent];
    if ([self.navigationController.viewControllers count]>1) {
        UIBarButtonItem *item=[[UIBarButtonItem alloc]initWithTitle:@"Root"
                                                              style:UIBarButtonItemStylePlain
                                                             target:self action:@selector(actionRoot:)];
        self.navigationItem.rightBarButtonItem=item;
        
    }

}

Для того, чтобы загрузить контроллер из сторибоард, а не создать с помощью конструктора, мы в функции 

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

обрабатывающей нажатие на строку заменим

//        TADirectoryViewController *controller=[[TADirectoryViewController alloc] initWithFolder:filePath];
//        [[self navigationController]  pushViewController:controller animated:YES];
 на 
      TADirectoryViewController* vc=[storyboard instantiateViewControllerWithIdentifier:@"TADirectoryViewController"];
        vc.path=filePath;
        [self.navigationController pushViewController:vc animated:YES];


Еще один вариант сделать вызов контроллера из самого контроллера с помощью сторибоард
Нужно поместить кнопку uibarbutton не на сам контроллер, а на панель над контроллером и связать эту кнопку с самим контроллером.
Кстати xCode выдает ворнинг на такой способ добавления, т.е. им лучше не пользоваться

Push segues are deprecated since iOS 8.0

Ссылке Segue даем идентификатор navigateDeep.
Так же добавляем новое property  
@property (strong,nonatomic) NSString * selectedPath;


Заменяем предыдущий абзац кода на

//        UIStoryboard *storyboard=self.storyboard;
//        TADirectoryViewController* vc=[storyboard instantiateViewControllerWithIdentifier:@"TADirectoryViewController"];
//        vc.path=filePath;

//        [self.navigationController pushViewController:vc animated:YES];


        self.selectedPath=filePath;


        [self performSegueWithIdentifier:@"navigateDeep" sender:nil ];

Определяем метод, который вызывается перед переходом по Segue

 -(void) prepareForSegue:(nonnull UIStoryboardSegue *)segue sender:(nullable id)sender{
    NSLog(@"prepareForSegue %@",segue.identifier);
    
    if([segue.identifier isEqualToString:@"navigateDeep"]){
        TADirectoryViewController *destinationController=segue.destinationViewController;
        TADirectoryViewController *sourceController=segue.sourceViewController;
        destinationController.path=sourceController.selectedPath;

    }
    
}

Если определить метод

-(bool) shouldPerformSegueWithIdentifier:(nonnull NSString *)identifier sender:(nullable id)sender{
    NSLog(@"shouldPerformSegueWithIdentifier identifier:%@",identifier);
    return YES;

}

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


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

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{

    if ([self isDirectoryAtIndexPath:indexPath]) {
        return 40.f;
    }else{
        return 80.f;
            }
 }

Для настройки ячеек таблицы из storyboard  UITableController настраиваем единственно созданную с контроллером ячейку.
Определяем фотографию папки и идентификатор FolderCell
Затем добавляем новую ячейку на контроллер типа UITableViewCell
Определяем 3 Label вставляем фотографию и кнопку.
Определяем идентификатор FileCell.
Создаем новый файл-класс тоже же типа.
Создаем оутлеты, которые соответствуют Label
@property (weak, nonatomic) IBOutlet UILabel *nameLabel;
@property (weak, nonatomic) IBOutlet UILabel *sizeLabel;

@property (weak, nonatomic) IBOutlet UILabel *dateLabel;



Заменяем старый код функции на новый, с учетом того, что ячейки загружаются из сторибоард


- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    
    static NSString *fileIdentifier=@"FileCell";
    static NSString *folderIdentifier=@"FolderCell";

    NSString *fileName=[self.contents objectAtIndex:indexPath.row];
    if ([self isDirectoryAtIndexPath:indexPath]) {
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:folderIdentifier];
        cell.textLabel.text=fileName;
        return cell;

        
    }else{
        NSString *filePath=[self.path stringByAppendingPathComponent:fileName];
        NSDictionary * attributes=[[NSFileManager defaultManager]attributesOfItemAtPath:filePath error:nil];
        TAFileCell* cell = [tableView dequeueReusableCellWithIdentifier:fileIdentifier];

        cell.nameLabel.text=fileName;
        cell.sizeLabel.text=[self fileSizeFromValue:[attributes fileSize]];
        static NSDateFormatter *dateFormatter=nil;
        if (!dateFormatter){
            dateFormatter=[[NSDateFormatter alloc]init];
            [dateFormatter setDateFormat:@"MM/dd/yyyy hh:mm a"];
        }
        cell.dateLabel.text=[dateFormatter stringFromDate:[attributes fileModificationDate]] ;
        
        return cell;

    }

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

-(NSString*) fileSizeFromValue:(unsigned long long) size{
    static NSString *unit[]={@"B",@"KB",@"Mb",@"Gb",@"TB"};
    static  int unitsCount=5;
    int index=0;
    
    double fileSize =(double) size;
    while (fileSize>1024&&index<unitsCount) {
        fileSize/=1024;
        index++;
    }
    return [NSString stringWithFormat:@"%.2f %@",fileSize,unit[index]];
}

Для определения ячейки на которой была нажата кнопка создаем дополнительную категорию к классу UIView

#import "UIView+UITableViewCell.h"

@implementation UIView (UITableViewCell)

-(UITableViewCell*) superCell{

    if(!self.superview) {
        return nil;
       }
    
    if ([self.superview isKindOfClass:[UITableViewCell class]]) {
        return (UITableViewCell*)self.superview;
    }

    return [self.superview superCell]; ;
}


@end

И обработчик  нажатия на кнопку с выводом секции и ряда нажатия

- (IBAction)actionInfo:(UIButton *)sender {
    NSLog(@"ActionInfo");
    UITableViewCell *cell=[sender superCell];
    if (cell) {
        NSIndexPath*indexPath=[self.tableView indexPathForCell:cell];
        NSLog(@"action %@ %@",@(indexPath.section),@(indexPath.row));
        
    }
}

Файл с исходниками к уроку 34.

Файл с тестовым заданием  без часов.
Файл с тестовым заданием с часами.

Домашнее задание к уроку 33-34

Я рекомендую вам немного задержаться на этом уроке и снова попрактиковать таблицы. Также будет довольно таки неплох о если вы углубитесь в NSFileManager

Ученик.

1. Добавьте возможность создавать директории
2. Добавьте возможность удалять файлы и папки

Студент

3. Сортируйте файлы и папки, сверху должны быть папки, снизу файлы, сортировка по имени
4. Не показывайте скрытые файлы

Мастер

5. В detailedTextField каждой ячейки файла, выводите размер файла.

Супермен

6. Тем же способом выводите размер папки (размер папки придется считать рекурсивно :) )

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

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