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. Т.е. Мы загрузили рисунок, получили имя файла, отсекли его расширение.

24 мар. 2014 г.

The Flat Project

The Flat Project - плоские иконки для OS X приложений. В наборе 34 иконки в формате .png и .icns

В набор вошли такие приложения:
Word, Excel, Powerpoint, Photoshop, Illustrator, Indesign, Pages, Numbers, Keynote, iMovie, iPhoto, iTunes, Garageband, Finder, Appstore, Facetime, Calendar, Contacts, Game Center, iBooks, Preview, Launchpad, Mail, Maps, Messages, Notes, Photo Booth, Reminders, Safari, Systemprefs, Trash, Xcode, Logic, Final Cut.



23 мар. 2014 г.

NSPopover и detachableWindowForPopover пример

NSPopover является анимированный всплывающий компонент, который показывает загруженные файлы в вэб-браузере Safari. Такой компонент был добавлен ​​в Mac OS X Lion. Если вы когда-нибудь задумывались, как добавить его в код, то это очень просто.

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

Начнем. У нас есть окно. Добавим на окно нашей формы кнопку, по которой мы будем вызывать наш popover.


Также добавим NSView, на котором разместим иконку (также можно любой контрол, будь-то кнопку или текстовое сообщение).


Найдем в списке библиотеки объектов Popover и перетянем в наш список объектов (вместе с Popover автоматически будет перетянут и Popover View Controller).

Теперь свяжем наш NSView с Popover View Controller и выберем оутлет view.
Также создадим для кнопки экшен, из которой будет вызываться popover.

- (IBAction)showPopOver:(id)sender;

И создадим связку нашего Popover (в списке наших объектов) с оутлетом *popover.


@property (assign) IBOutlet NSPopover *popover;


В экшен кнопки добавим такой код:


[[self popover] showRelativeToRect:[sender bounds] ofView:sender preferredEdge:NSMaxXEdge];


Здесь макрос NSMaxXEdge будет показывать наш popover вертикально сверху от вызывающего контрола или снизу (в зависимости от вертикального расположения нашего окна). Если нужно показывать слева или справа - NSMaxYEdge.

Выделим наш Popover и в инспекторе атрибутов и в Popover -> Behavior выберем Transient. Этот атрибут будет позволять popover`у удаляться если кликнуть где-то в другом месте окна приложения.



Вот что должно получится:
detachableWindowForPopover - popover который при перетаскивании может преобразовываться в окошко NSWindow. Все тоже очень просто. Добавляем в наш список объектов NSWindow. Размещаем на нем что-то, можно ту же иконку.  Выделяем наш popover и связываем delegate (popover`а) с App Delegate - объектом (в списке наших объектов). Теперь в хидер файл добавим NSPopoverDelegate делегат, который позволит нам создавать окно из popove`ра.


#import <Cocoa/Cocoa.h>

@interface AppDelegate : NSObject <NSApplicationDelegate, NSPopoverDelegate>


Связываем наше добавленное окно с оутлетом, по которому мы будем обращаться к этому окну.


@property (unsafe_unretained) IBOutlet NSWindow *popWindow;


Добавляем в .m - файл метод detachableWindowForPopover:


- (NSWindow *) detachableWindowForPopover:(NSPopover *)popover {
    
    return [self popWindow];
}


В принципе все. Должно работать. При появлении popove`ра перетаскиваем его в любое место и создается наше окошко.


21 мар. 2014 г.

Добавить N-дней к NSDate

Есть два метода для достижении этой цели. Первый, является быстрым и немного грязноватым. И второй, который более безопасен в расчетах.
Первый способ включает в себя просто добавление количества дней в секундах:

NSDate *now = [NSDate date];
int daysToAdd = 50;  // или 60 :-)
NSDate *newDate1 = [now addTimeInterval:60*60*24*daysToAdd];
NSLog(@"Быстрый расчет: %@", newDate1);

Данный метод имеет ряд ограничений. Он не заботиться о переходе на летнее или зимнее время. Таким образом, Вы обходите границы DST, в конечном итоге получите результат, который расходится между желаемым результатом вычисления на один час и в худшем случае на день.
Таким образом, следующий метод использует класс NSDateComponents и григорианский календарь, чтобы правильно добавить количество дней и корректно рассчитать результат:

NSDate *now = [NSDate date];
int daysToAdd = 50;  // или 60 :-) 
// используем NSDateComponents
NSDateComponents *components = [[[NSDateComponents alloc] init] autorelease];
[components setDay:daysToAdd];
 
// Используем календарь
NSCalendar *gregorian = [[[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar] autorelease];
 
NSDate *newDate2 = [gregorian dateByAddingComponents:components toDate:now options:0];
NSLog(@"Правильный расчет: %@", newDate2);

Функции даты в Objective-C являются чрезвычайно мощными, но время от времени их трудно понять, для людей, которые являются начинающими программистами.

Конвертация NSString в массив символов

Один из способов сделать это - просто получить один символ подстроки из заданной полной строки:

NSString *s = @"Hello World";
int i;
NSMutableArray *m = [[NSMutableArray alloc] init];
 
for (i = 0;i < [s length]; i++) {
    [m addObject:[s substringWithRange:NSMakeRange(i, 1)]];
}
 
NSLog([m description]);