1 апр. 2014 г.

NSFileManager, NSFileHandle и NSData классы (миниобзор)

Foundation Framework предоставляет три класса, которые необходимы, для работы с файлами и каталогами:

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

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

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

NSUserDefaults - сохранение настроек приложения

Использование NSUserDefaults - самый легкий и самый простой способ (существуют и иные способы) сохранения настроек Вашего приложения.

NSUserDefaults - это простой список данных (аналог plist), с помощью которого приложение может хранить простые данные. Нет никаких ограничений на размер данных (помимо собственных ограничений заданных приложением), не используйте этот класс для хранения очень большого объема данных. Файл записывается и считывается атомарно (т.е. целиком), т.е., чем больше данных, которые находятся в файде, тем дольше они будут считыватся. Тем не менее, этот класс подходит для того, чтобы хранить параметры, настройки, и т.п..

К сожалению NSUserDefaultsимеет ряд ограничений по переменным, которые он может сохранять. Это:
– NSArray
– NSData
– NSDictionary
– NSNumber
– NSString

Кроме того, NSArray или NSDictionary должны только содержать упомянутые выше типы (возможно вложение NSArray или NSDictionary). Другие пункты, которые соответствуют протоколу NSCoding, могут быть заархивированы как NSData, т.е., Вы можете сохранить их в переменных. Будем использовать ключи для доступа к данным.

Все что нужно сделать, это загрузить данные в NSUserDefault:

NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setInteger:9001 forKey:@"HighScore"];
[defaults synchronize];

Чтение данных из NSUserDefault:

NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSInteger theHighScore = [defaults integerForKey:@"HighScore"];

Вот и все. Вы создаете объект типа NSUserDefaults, создаете переменную в которою будете загружать данные по ее типу и определенному ключу.

31 мар. 2014 г.

Компонент интерфейса в заголовке окна

В обычной программе за отрисовку окна отвечает недокументированный класс NSThemeFrame.
Создаем окно с оутлетом window на это окно. Добавляем NSView и тоже создаем на него привязку оутлет:
Добавляем на вьюху компонент NSPopupButton, убираем флажок Bordered в Инспекторе Атрибутов. В AppDelegate.h должен быть такой код:

#import <Cocoa/Cocoa.h>

@interface AppDelegate : NSObject <NSApplicationDelegate> {
    
    IBOutlet NSWindow *window;
    IBOutlet NSView *itemView;
}

- (void)composeInterface;

@end


AppDelegate.m:

#import "AppDelegate.h"

@implementation AppDelegate

- (void)awakeFromNib
{
[self composeInterface];
}

- (void)composeInterface
{
// Получаем указатель на фрейм окна
NSView *themeFrame = [[window contentView] superview];
NSRect contentWindowFrame = [themeFrame frame]; // размер фрейма окна
NSRect itemViewFrame = [itemView frame]; // размер фрейма вьюхи
NSRect newFrame = NSMakeRect(
                                 contentWindowFrame.size.width - itemViewFrame.size.width, // x позиция
                                 contentWindowFrame.size.height - itemViewFrame.size.height, // y позиция
                                 itemViewFrame.size.width, // ширинв
                                 itemViewFrame.size.height); // высота
    
[itemView setFrame:newFrame];
[themeFrame addSubview:itemView];
}

@end


Вместо NSPopupButton можна добавить любой элемент на вьюху. Главное чтобы высота компонента не была больше чем сам Title Bar.
Дальше все просто. Делаем оутлет на PopupButton, и делаем с ним все что душе угодно.

29 мар. 2014 г.

#pragma mark

Директива #pragma mark добавляет строку в "Function menu". 

'#pragma mark -' - добавляет горизонтальную линию в меню;
'#pragma mark labelname' - добавляет заголовок.

27 мар. 2014 г.

26 мар. 2014 г.

View-based NSTableView пример

Виды на основе view-based таблицы, обеспечивают богатые возможности во время проектирования. Сам по себе NSTableCellView отображает ImageView и textField. Но отличающейся особенностью этого рода view-based ячеек от cell-based ячеек, является размещение в ячейке разных видов компонентов интерфейса.

Итак, начнем. Создадим новый проект в Xcode. Добавим на форму NSTableView. Выберем нашу таблицу NSTableView (Помните, что таблица сама по себе состоит из набора компонентов - NSScrollView -> NSClipView -> NSTableView-> NSTableColumn -> NSCell). И в Инспекторе Атрибутов в секции Table View -> Content Mode, вместо Cell Based выберем View Based. Выставим количество колонок равным 1-й. Удалим в таблице строчку Text View или Text, и вместо этой строчки добавим View-based строчку (В наборе компонентов интерфейса находим Image & Text Table Cell View, и перетаскиваем в нашу таблицу). Выделяем вставленную строку и в Size Inspector устанавливаем высоту строки равным 50px. Далее выбираем иконку в нашей ячейке и в Инспекторе Атрибутов в секции Image Cell -> Image наберем NSApplicationIcon (Вы можете растянуть иконку как Вам угодно, и скомпоновать с текстовой строкой в разных положениях в пределах View-строки). Напоследок выделяем NSTableColumn, и в Identity Inspector -> Identity -> Identifier пропишем идентификатор колонки "MainCell". По этому идентификатору будем обращаться к колонке не по ее ID, а по ее имени, что естественно проще запомнить. Должно получится что-то вроде этого:

Опять выделим NSTableView. Перейдем в Connections Inspector и свяжем dataSource и delegate с App Delegate - объектом.

Суть задачи состоит в том, чтобы из стандартного приложения iChat вытянуть флаги стран - собеседников и вывести их в таблицу отобразив иконку флага и название страны.

В AppDelegate.h добавляем делегаты NSTableViewDataSource и NSTableViewDelegate, потому что Мы будем использовать методы делегатов для работы с таблицами. Основные методы, без которых не будет работать таблица описаны в статье Урок по NSTableView.

@interface AppDelegate : NSObject <NSApplicationDelegate, NSTableViewDataSource, NSTableViewDelegate>

Открываем AppDelegate.m и пишем код (код с пояснениями):

#import "AppDelegate.h"

@implementation AppDelegate {
    
    NSMutableArray *tableContents;
}

- (void)awakeFromNib {
    
    tableContents = [[NSMutableArray alloc] init];
    
    // Устанавливаем путь к файлам - флагам
    NSString *pathFlags = @"/Library/Application Support/Apple/iChat Icons/Flags";
    
    // Инициализируем файл - менеджер для работы с файлами
    NSFileManager *filesOfFlags = [NSFileManager defaultManager];
    
    // Получает список поддиректорий директории или список файлов
    NSDirectoryEnumerator *dEnumerator = [filesOfFlags enumeratorAtPath:pathFlags];
    
    // Переменная в которой будет хранится каждый файл
    NSString *file;
    
    // Проходим в цикле по всем файлам и добавляем созданные объекты в массив
    while (file = [dEnumerator nextObject]) {
        
        // Получаем полное имя файла с расширением
        NSString *filePath = [pathFlags stringByAppendingFormat:@"/%@", file];
        
        // Вытягиваем рисунок с файла и имя файла без расширения
        // Инициализируем для каждого изображения и имени ключи по которым будем обращаться
        // к компонентам объекта
        NSDictionary *dictObj = @{@"image": [[NSImage alloc] initByReferencingFile:filePath],
                                  @"imageName": [file stringByDeletingPathExtension]};
        
        // Добавляем объект словаря в массив
        [tableContents addObject:dictObj];
    }
}

- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView {
    
    // Возвращаем количество строк в массиве для выделения количества строк в таблице
    return [tableContents count];
}

- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
    
    // Получаем входящую строку row (ее индекс)
    NSDictionary *flags = tableContents [row];
    
    // Определяем идентификатор таблицы (MainCell)
    NSString *colID = [tableColumn identifier];
    
    if ([colID isEqualToString:@"MainCell"]) {
        
        // Получить ячейку с идентификатором MainCell если она существует
        NSTableCellView *tCell = [tableView makeViewWithIdentifier:@"MainCell" owner:self];
        
        // Заполняем ячейки таблицы
        [tCell.textField setStringValue:flags[@"imageName"]];
        [tCell.imageView setImage:flags[@"image"]];
        
        // Возвращаем готовую ячейку, иначе нихрена не делаем
        return tCell;
    }
    
    return nil;
}

@end

Если все правильно сделано, должно в итоге получится во такое приложение:

В папке (к которой Мы обращаемся через код) лежат файлы типа .png. Т.е. Мы загрузили рисунок, получили имя файла, отсекли его расширение.