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

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]);

20 мар. 2014 г.

Добавление числа в массив / Add number to Array

Обычно в массив типа NSArray или NSMutableArray нельзя добавить простое число или структуру, а только объект. Значит (в нашем примере) преобразуем наше простое число (например int) в объект. Число можно преобразовать как с помощью NSString, так и с помощью NSNumber.

NSMutableArray *myArray = [NSMutableArray arrayWithCapacity:30]; // должен быть мутабельным
 
for (int x = 0; x < 30; x++) {
    // добавить как NSString
    [myArray addObject:[NSString stringWithFormat:@"%d", x]];
 
    // добавить как NSNumber
    [myArray addObject:[NSNumber numberWithInt: x]];
}
 
// результат
NSLog([myArray description]);

Для обоих случаев используемые классы возвращают число как объект.

NSString удаление пробелов в начале и конце текста

Удаление ненужных пробелов по бокам текста:

NSString *string = @" spaces in front and at the end ";
NSString *trimmedString = [string stringByTrimmingCharactersInSet:
                                  [NSCharacterSet whitespaceAndNewlineCharacterSet]];
NSLog(trimmedString)

Хотя такая стандартная задача может выглядеть с чрезмерно большим количеством кода,но при этом Вы получаете множество дополнительных функций для операций с текстом. NSCharacterSet и его набор некоторых методов:

- alphanumericCharacterSet
- capitalizedLetterCharacterSet
- controlCharacterSet
- decimalDigitCharacterSet
- decomposableCharacterSet
- illegalCharacterSet
- letterCharacterSet
- lowercaseLetterCharacterSet
- newlineCharacterSet
- nonBaseCharacterSet
- punctuationCharacterSet
- symbolCharacterSet
- uppercaseLetterCharacterSet
- whitespaceAndNewlineCharacterSet
- whitespaceCharacterSet

19 мар. 2014 г.

Как хранить BOOL - переменную как NSNumber

Создать NSNumber - переменную с булевой можно таким способом:

BOOL myBool = YES;
NSNumber *boolNumber = [NSNumber numberWithBool:myBool];
NSLog(@"boolNumber is %@", boolNumber); // выдаст 1 для YES и 0 для NO

Технически это аналог инициализации NSNumber с помощью литералов @1 и @0
И обратная операция с NSNumber в BOOL:

BOOL anotherBool = [NSNumber numberWithBool:YES];
NSLog(@"anotherBool is %i", anotherBool); // выдаст 1 для YES и 0 для NO

18 мар. 2014 г.

Badges на иконке Вашего приложения (дополнение) / NSlider and docTile

Дополнение к статье заключается в том чтобы в реальном времени изменять данные в бэйджике на иконке приложения вместе с некими изменениями данных в Вашем приложении.

Нужно добавить в методе - (void)applicationDidFinishLaunching:(NSNotification *)aNotification строчку после [sliderTextLabel setIntValue:[slider intValue]]; :

[[NSApp dockTile] setBadgeLabel:[NSString stringWithFormat:@"%i",[slider intValue]]];

Эта строка проинициализирует начальное состояние числового значения слайдера и выведет его в бейдж.


Также в нашем экшене - (IBAction)sliderChange:(id)sender после строки [sliderTextLabel setIntValue:[slider intValue]]; Вставить ту же строку, что Вы вставляли в - (void)applicationDidFinishLaunching:(NSNotification *)aNotification 

При изменении положения ползунка, данные будут меняться и в нашем бэйдже. Код:

[NSString stringWithFormat:@"%i",[slider intValue]]

приводит целочисленное значение в тип "строка", тем самым обрезает плавающую точку. Если вместо этого написать просто [slider stringValue], то будет отображаться число с плавающей точкой.

17 мар. 2014 г.

16 мар. 2014 г.

15 мар. 2014 г.

KVC (Key-Value Coding) Ключ - Значение

Ключ-значение кодирования представляет собой механизм для доступа к свойству объекта косвенно, с помощью строк для идентификации свойств, а не через вызов аксессора или доступ к ним непосредственно через переменных экземпляра. Что это значит. Допустим у Вас есть таблица с множеством колонок, каждое имя колонки можно превратить в ключ и обращаться по ключу вместо индекса колонки, ведь так проще, да и запомнить название колонки проще чем ее индекс.

В данном примере мы не будем работать с таблицами, а реализуем некий класс Books (Книги). Основное что нам требуется для книги это "Автор книги", "Название книги" и "Количество страниц в книге".


Итак добавили новый класс наследованный от NSObject:

Books.h :

#import <Foundation/Foundation.h>

@interface Books : NSObject

@property (strong) NSString *bookAuthor;
@property (strong) NSString *bookName;
@property int pageCount;

@end

Books.m - оставим пустым, т.к. остальное мы реализуем в классе AppDelegate

#import "Books.h"

@implementation Books

@end

В AppDelegate.h создадим массив booksArray со списком книг. Ведь у нас будет не одна книга. Также не забудем подключить наш класс Books.

#import <Cocoa/Cocoa.h>
#import "Books.h"

@interface AppDelegate : NSObject <NSApplicationDelegate> {
    NSMutableArray *booksArray;
}

@property (assign) IBOutlet NSWindow *window;
- (IBAction)printListBooks:(id)sender;

@end

На нашу форму поместим кнопку и сделаем связь кнопки с методом printListBooks.

В AppDelegate.m создадим несколько объектов книг и добавим их в массив который будет хранить список наших книг.

#import "AppDelegate.h"

@implementation AppDelegate

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
    
    booksArray = [[NSMutableArray alloc] init];
    
    // Добавляем наши книги-объекты в массив обычным способом
    
    Books *firstBook = [[Books alloc] init];
    [firstBook setBookAuthor:@"Т.Г. Шевченко"];
    [firstBook setBookName:@"Кобзарь"];
    [firstBook setPageCount:283];
    
    [booksArray insertObject:firstBook atIndex:[booksArray count]];
    
    Books *secondBook = [[Books alloc] init];
    [secondBook setBookAuthor:@"И. Франко"];
    [secondBook setBookName:@"Городские повести"];
    [secondBook setPageCount:478];
    
    [booksArray insertObject:secondBook atIndex:[booksArray count]];
}

Организуем код для кнопки, которая будет выдавать список объектов из массива книг.

- (IBAction)printListBooks:(id)sender {
    
    for (Books *currentBook in booksArray) {
        NSLog(@"Книга: %@, Автор книги: %@, К-во страниц: %i",
              [currentBook bookName],
              [currentBook bookAuthor],
              [currentBook pageCount]);
    }
}
@end

Запустим и получим результат:


А как же KVC ? Все довольно просто. Добавим еще одну книгу, но теперь будем обращаться не к переменным нашего объекта, а по ключу (аналогично названию переменным) и будем задавать ему значение:

 // Добавляем наши книги-объекты с помощью ключей (KVC)
    
    Books *threeBook = [[Books alloc] init];
    [threeBook setValue:@"Леся Украинка" forKey:@"bookAuthor"];
    [threeBook setValue:@"Боярыня" forKey:@"bookName"];
    [threeBook setValue:@"184" forKey:@"pageCount"];
    
    [booksArray insertObject:threeBook atIndex:[booksArray count]];

Здесь bookAuthor, bookName, pageCount являются ключами, а Value - значениями для каждого ключа. Запустим наше приложение:


Результат - добавлена третья книга в массив-список книг с помощью KVC.