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

2 июн. 2014 г.

Советы и Рекомендации: Протоколы

Бывает такая штука в протоколах как необязательные / опциональные методы @optional. При работе с объектами/классами, особенно в больших проектах, заведомо не знаешь реализован тот или иной необязательный метод в классе, например, другим разработчиком класса. При компиляции проекта, компилятор не укажет на это даже в варнингах (warnings). После запуска приложения и обработке входящих объектов, в которых из них не будет реализации опциональных методов, естественно приложение упадет. Для проверки реализации опционального метода в классе/объекте рекомендуется использовать селекторы, т.е. вся отвественность ложиться на разработчика:

[objectName respondToSelector:@selector(protocolMethodName)]

т.е. здесь мы проверяем реализован ли в объекте/классе objectName метод описанный в протоколе protocolMethodName. Результат булевый YES | NO.

Как узнать, использует ли объект/класс протокол (подключен ли протокол)?:

[objectName conformsToProtocol:@protocol(protocolName)]

Результат булевый YES | NO.

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"];
    
}

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