Показаны сообщения с ярлыком NSTableView. Показать все сообщения
Показаны сообщения с ярлыком NSTableView. Показать все сообщения

3 июн. 2014 г.

Swift: Простой пример NSTableView (@Action)

Усложним предыдущий пример простым добавлением строк (Добавим кнопки):
Сделаем @Action на кнопки, и напишем такой код:

@IBAction func addDataToTableView(sender : NSButton) {
        
        tableArrayData.insert("New String", atIndex: 0)
        tableView.reloadData()
        
    }
    
    @IBAction func removeDataFromTableView(sender : NSButton) {
        
        if tableArrayData.count != 0 {
         
            tableArrayData.removeAtIndex(0)
            tableView.reloadData()
            
        }
        
    }

Получится вот так:


Swift: Простой пример NSTableView

Только что поюзав первый раз Xcode 6 Beta (запустил с третьего раза) по памяти наваял табличку. Скажу сразу что очень плохо работает автодополнение. Все делаем как и в предыдущих примерах по Cell-Based таблицах. Выделяем таблицу, делаем связку dataSource и delegate с AppDelegate. А дальше код:

import Cocoa

class AppDelegate: NSObject, NSApplicationDelegate, NSTableViewDataSource {
                            
    @IBOutlet var window: NSWindow
    @IBOutlet var tableView: NSTableView
    
    var tableArrayData = ["Dad", "Mom", "Son", "GrandMother", "Cat", "Bird", "Kakashka"]


    func applicationDidFinishLaunching(aNotification: NSNotification?) {
        // Insert code here to initialize your application
    }

    func applicationWillTerminate(aNotification: NSNotification?) {
        // Insert code here to tear down your application
    }

    func numberOfRowsInTableView(tableView: NSTableView!) -> Int    {
        
        return tableArrayData.count
    }
    
    func tableView(tableView: NSTableView!, objectValueForTableColumn tableColumn: NSTableColumn!, row: Int) -> AnyObject! {
        
        return tableArrayData[row]
    }

}

27 апр. 2014 г.

View-based NSTableView на основе ячеек из NSView (Злосчастная кнопка). Продолжение

В предыдущей статье о View-based NSTableView на основе ячеек из NSView не было реализации по использованию кнопки на виде (замечание в комментарии читателя). Порывшись в поиске (вместе с несуразными лексическими словосочетаниями), хоть какого-то решения так и не было найдено. Было решено использовать делегат и дать право AppDelegate кликать на эту злосчастную кнопку. 
Все бы ничего, но как узнать индекс этой чертовой вьюхи, на которой находится эта кнопка? Подумав, поломав голову вместе с мозгами, решил по нажатию кнопки AppDelegate-м передавать ему эту вьюху. Ведь все они разные, т.к. под каждую строку выделена память, естественно и адреса их будут разные.
Создаем делегат - добавляем в проект новый protocol-файл. Объявим в нем метод который будет передавать право тыканья этой кнопки AppDelegate-у.

#import <Foundation/Foundation.h>

@protocol ButtonControllerProtocol <NSObject>

@required
- (void) viewButtonClicked: (NSButton *) clickedButton view: (NSView *) viewCell;

@end

Далее в ViewController.h добавим экшен на кнопку, подключим AppDelegate и укажем какой метод использовать при нажатии этой кнопки.

- (IBAction)buttonInfo:(id)sender {
    
    AppDelegate *myAppDelegate = (AppDelegate *)[[NSApplication sharedApplication] delegate];
    [myAppDelegate viewButtonClicked:sender view:[self view]];
    
}

В AppDelegate.h подключим созданный протокол и пропишем делегат:

#import "ButtonControllerProtocol.h"

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

Реализуем протокольный метод:

- (void)viewButtonClicked: (NSButton *) clickedButton view: (NSView *) viewCell {
    NSLog (@"click");
}

Все бы хорошо, метод обрабатывается, выдает определенный результат, а дальше-то что? А дальше нам якобы нужно как-то менять данные на вьюхе. Вот здесь-то и пришлось помучатся. Хотя все оказалось немного проще. Мы помним что окно как главный вид, состоит из подвидов разной сложности, т.е. проще говоря на нашей вьюхе-подвиде тоже есть подвиды (subviews) - nsimageview, nstextfield и т.д. Остается лишь получить к ним доступ. Получим подвиды на вьхе-строке:


Загоним эти объекты в массив и потом создадим указатели на эти объекты и будем обращаться к непосредственно новым объектам-подвидам:

- (void)viewButtonClicked: (NSButton *) clickedButton view: (NSView *) viewCell {

    // Получаем строку-вид в нашей таблице
    NSView *clickedView = [clickedButton superview];
    
    // Получаем список подвидов в строке-виде
    NSArray *viewObjectsArray = [NSArray arrayWithArray:[clickedView subviews]];
    
    // Коннектимся к подвиду и задаем значение
    NSImageView *viewObjectImage = [viewObjectsArray objectAtIndex:0];
    [viewObjectImage setImage:[NSImage imageNamed:@"Slice 1"]];
    
    NSTextField *viewObjectFirstTextField = [viewObjectsArray objectAtIndex:1];
    [viewObjectFirstTextField setStringValue:@"Slice 1"];
    
}

Вот такое вот нетривиальное, не претендующее ни на что, решение. Если есть более человеческое решение - прошу указать в комментариях.

14 апр. 2014 г.

View-based NSTableView на основе ячеек из NSView

Суть построения View-based таблицы заключается в том, чтобы вместо обычных строк, состоящих из NSImageView и NSTextField (как показано в предыдущем примере), создать кастомную (свою модель компонентов в строке) строку. И здесь как всегда на помощь приходит NSView, а точнее NSViewController - класс.

Создаем новый проект. Назовем его ViewBasedNSTableViewWithNSView (Вы можете его назвать по своему, суть этого не меняется). Как и в предыдущем примере добавим таблицу, сделаем строку View-based (прочтите предыдущий пример, здесь в этом посте я упущу эти тонкости). Далее добавим новый класс наследуемый от NSViewController c xib-формой. Добавим компоненты интерфейса на  xib-форму. Я добавил два Label, кнопку с изображением ImageCell и ProgressBar. Т.е. можно накидать компоненты по своему желанию и усмотрению, а также целевому типу. Должно получится что-то из этого:


В ViewController.h сделаем оутлеты к нашим компонентам и к самой вьюхе и парочку методов.


#import <Cocoa/Cocoa.h>

@interface ViewController : NSViewController {
    
    IBOutlet NSImageView *iconView;
    IBOutlet NSTextField *messageLabelOne;
    IBOutlet NSTextField *messageLabelTwo;
    NSView *_mainView;
    
}

- (void) setStringValueForFirstTextFields: (NSString *) stringValue;
- (void) setStringValueForSecondTextFields: (NSString *) stringValue;
- (void) setIconForRowOnView: (NSImage *) iconContent;

@end


В ViewController.m распишем код для наших методов, также по умолчанию зададим текст для лэйблов:


#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        // Initialization code here.
    }
    return self;
}

- (id)init {
    
    [self.view addSubview: _mainView];
            
    [messageLabelOne setStringValue:@"Message One"];
    [messageLabelTwo setStringValue:@"Message Two"];
    
    return self;
}

- (void) setStringValueForFirstTextFields: (NSString *) stringValue {
    
    [messageLabelOne setStringValue:stringValue];
    
}

- (void) setStringValueForSecondTextFields: (NSString *) stringValue {
    
    [messageLabelTwo setStringValue:stringValue];
    
}

- (void) setIconForRowOnView: (NSImage *) iconContent {
    
    [iconView setImage:iconContent];
    
}

@end


Подключим хидер контроллера-модели ячейки в AppDelegate.h. Объявим массив в котором будем хранить вьюхи-объекты, свяжем оутлет переменной NSTableView с таблицей на форме, добавим объект-контроллер (по которому будем обращаться к индексу массива объектов и получать наш объект для текущего индекса строки. Помните?, я уже упоминал что строки в таблицу добавляются строчка-за-строчкой.).


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

@interface AppDelegate : NSObject <NSApplicationDelegate, NSTableViewDelegate, NSTableViewDataSource> {
    
    NSMutableArray *objArray;
    ViewController *vController;
}

@property (assign) IBOutlet NSWindow *window;
@property (weak) IBOutlet NSTableView *tableView;

@end


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


#import "AppDelegate.h"

@implementation AppDelegate

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
    // Insert code here to initialize your application
}

- (void)awakeFromNib {
    
    objArray = [[NSMutableArray alloc] init];
    
    ViewController *oneController = [[ViewController alloc] init];
    ViewController *twoController = [[ViewController alloc] init];
    ViewController *treeController = [[ViewController alloc] init];
    ViewController *fourController = [[ViewController alloc] init];
    ViewController *fiveController = [[ViewController alloc] init];
    ViewController *sixController = [[ViewController alloc] init];
    ViewController *sevenController = [[ViewController alloc] init];
    
    [oneController setStringValueForFirstTextFields:@"Hello World"];
    [oneController setStringValueForSecondTextFields:@"Hello To You"];
    [oneController setIconForRowOnView:[NSImage imageNamed:@"layers.icns"]];
    
    [twoController setStringValueForFirstTextFields:@"Hello World"];
    [twoController setStringValueForSecondTextFields:@"Hello To You"];
    [twoController setIconForRowOnView:[NSImage imageNamed:@"Slice 1.icns"]];
    
    [objArray addObject:oneController];
    [objArray addObject:twoController];
    [objArray addObject:treeController];
    [objArray addObject:fourController];
    [objArray addObject:fiveController];
    [objArray addObject:sixController];
    [objArray addObject:sevenController];
}

- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView {
    
    return [objArray count];
}

- (id)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
    
    vController = [objArray objectAtIndex:row];
    return [vController view];
    
}

@end


Должна получится вот такая каша:
В принципе все. Самое главное - это понять как работает делегирование в таблице и сама таблица. Все остальное дело техники. При моей занятости (работа, семья) на понимание всей этой каши (по работе с таблицами в целом) ушло месяц-полтора. Если посчитать время уделенное на разборку всего этого, то общая затраченная сумма при интенсивном изучении заняла бы всего-то меньше недели.

Продолжение

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

25 февр. 2014 г.

Objective-C протоколы. Часть 1

Протоколы описывают методы, которые могут быть реализованы каким-либо классом. Цели для которых используются протоколы: 

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

Формальные протоколы

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

@protocol ProtocolName
     method declarations 
@end

За словом @protocol следует перечисление методов, также как и в обычном объявлении методов класса. Протокол может быть унаследован от произвольного количества других протоколов:

@protocol MyProtocol <Protocol1, Protocol2, Serializable, Drawable>

Добавление поддержки протокола к классу

Чтобы указать классу о поддержке определенного протокола необходимо включить заголовочный файл протокола в файл описания класса, и после в <скобках, через запятую>перечислить протоколы:

#import "protocolName.h"
 
@interface MyClass : SuperClass <protocolName>
     описание класса
@end
 
В файле реализации класса необходимо реализовать все методы которые описывает протокол! Но - в Objective-C 2.0 добавлены еще два новых модификатора @optional и @required:
 
@protocol asdfg
     - (void) readMy;
@optional
     - (void) saveReadMy;
     - (NSString*) loadReadMy;
@required
     - (NSInteger) countReadMy;
@end

Данный протокол показывает, что протокол asdfg дожен реализовать методы readMy и countReadMy, а методы saveReadMy и loadReadMy необязательны к реализации.
 
Более подробно о протоколах и их применению будет в следующей статье. Одно из применений протоколов можно посмотреть на примере табличных представлений NSTableView.

15 февр. 2014 г.

Урок по работе с NSTableVIew. Добавление данных

Для пользователя очень важно, чтобы данные были представлены в удобном виде. В этой статье мы рассмотрим как создается табличное представление данных в OS X. Урок предназначен для тех кто уже более-менее знаком с Objective-C, и протоколами. Для начала создадим новый проект в Xcode (Cocoa). Назовем его, например, TableViewAddDataTest. Добавим на нашу форму TableView контрол. Выставим размеры согласно нашей форме (окну).



Установим количество колонок в таблице равным 1-й (Attribute Inspector -> Columns). Дальше заходим в Connections Inspector и соединяем (делаем связь) нашему аутлету dataSource и делегату delegate с AppDelegate.

А теперь о главном. "Нельзя просто так взять и добавить строчку в таблицу напрямую", проще всего и быстрее будет пакетная передача объектов таблице, что чаще в роли пакетов объектов выступают массивы, в нашем случае NSArray (для одной колонки).

И так - поехали. В наш хидер-файл AppDelegate.h добавим протокол (делегат), объявим наш массив dataArray и объект myTableView через который будем работать с таблицей и будем использовать методы NSTableView класса:

//
//  AppDelegate.h
//  TableViewAddDataTest
//
//  Created by Sergey Krasiuk on 15.02.14.
//  Copyright (c) 2014 Sergey Krasiuk. All rights reserved.
//

#import <Cocoa/Cocoa.h>

@interface AppDelegate : NSObject <NSApplicationDelegate, NSTableViewDataSource>
{
    NSArray *dataArray;
    NSTableView *myTableView;
}

@property (assign) IBOutlet NSWindow *window;

@end

Главное что нужно помнить при работе с табличным представлением, это - два обязательных метода, без которых в таблице не появятся данные:


//  Нужно передать табличному представлению количество строк
//  для отображения данных, в нашем случае это колличество элементов в массиве.
- (NSInteger) numberOfRowsInTableView:(NSTableView *)tableView


//  Возвращает объект (данные которые будут отображаться в строке)
//  для каждой строчки row нашего табличного представления
- (id) tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row

А теперь понятным языком. В первый метод мы должны передать количество объектов в нашем массиве. На основе этого в таблице будет выделена память под каждую строчку:


- (NSInteger) numberOfRowsInTableView:(NSTableView *)tableView
{
    return [dataArray count];
}

Следующий шаг. Мы должны передать для каждой строчки таблицы данные которые хранятся в нашем массиве. Т.е. человеческим языком индекс строки row будет равен индексу элемента нашего массива данных. В нашем случае мы передадим объекты типа NSString для каждой строки:


- (id) tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
{
    NSString *arrayString = [dataArray objectAtIndex:row];
    return arrayString;
}


Проинициализируем наш массив (заполним его данными), т.к. пока что передавать нам в таблицу нечего (лучше всего инициализацию делать в методе init (в нашем случае), не в applicationDidFinishLaunching, при запуске массив не успеет заполнится данными и мы в таблице ничего не получим):


- (id) init
{
    self = [super init];
    
    if (self)
    {
        dataArray = [[NSArray alloc] initWithObjects:@"Первая строка", @"Вторая строка", @"Третья строка", @"Четвертая строка", @"Пятая строка", @"Шестая строка", nil];
    }
    
    return self;
}

Запускаем наш проект, и должно получится вот такое:
Нюанс в добавлении или удалении элементов (строк) из таблицы состоит в том чтобы вычислить выделенный объект (строку таблицы) и получить его индекс. Потом удалить/добавить объект под этим индексом из/в массива и вызвать в том же методе (в котором удаляем/добавляем объекты из массива) reloadTable. Табличное представление обновит данные из массива и покажет нужный результат:

- (void) tableViewSelectionDidChange: (NSNotification *) notification

Удачи в начинаниях. Еще один из примеров NSTableView


10 июн. 2013 г.

Простой пример NSTableView приложения с NSArrayController и NSUserDefaultsController

Сегодня я опишу один из простых примеров, который уже достал меня и забрал много времени, когда я пытался его реализовать! Я покажу Вам, как выполнить связь между NSTableColumn, NSArrayController и NSUserDefaultsController, не написав ни единой строчки кода.

Шаг 1: Создайте новый Cocoa-проект
Шаг 2: Дизайн ГУИ.

    Выберите MainMenu.xib, перетащите кнопки и таблицу из библиотеки объектов на окно и расставьте их в соотвествии как показано на скриншоте. Также добавьте из библиотеки объектов объект Array Controller, на панель xib. В моем случае я его переименовал в playersArrayController, который видно на скриншоте.

Шаг3: В окне настроек объектов (справа), выбираем меню Content Array binding. (Не забудьте включить состояние флажка на "Handles Content As Compound Value"



Шаг 4: Выставьте значение имени столбца Player (Model Key Path), в соответствии с рисунком ниже.


Шаг 5: И также для имени столбца Sport (Model Key Path), в соответствии с рисунком ниже.


Шаг 6: Свяжем действие add кнопки "+" с нашим Array Controller`ом.


Шаг 7: Такое же действие remove для кнопки "-" с нашим Array Controller`ом.


Шаг 8: Кнопку "Save" свяжем с действием Shared User Defaults Content.


    Финальный шаг - запустите программу. Теперь попробуйте в действии "+" и "-". Добавятся строки которые можно редактировать. А при нажатии на кнопку "Save", данные сохранятся. И после перезапуска приложения данные снова появятся в таблице.
 

    Пример кода можно скачать с github.com
    P.S. От автора. Переведенная статья, найдена на просторах сети.