Вызов IBAction в другом классе

В моем приложении, основанном на документах, есть окно с табличным представлением. Представление таблицы имеет источник данных, который указывает на класс типа NSObject (называемый HopBill), который включает в себя NSMutableArray (aHopBill) и необходимые методы представления таблицы. Все идет нормально.

Для добавления строк в табличное представление я добавил лист, которым управляет NSWindowController (называемый HopBillSheetController). При нажатии кнопки ОК в листе. На самом деле мне нужно сделать два IBActions (что невозможно): добавить строку в массив таблицы и закрыть лист. Я могу подключить кнопку OK на листе к NSWindowController (чтобы закрыть лист) или подключить его к NSObject (чтобы добавить строку в массив). Но я хочу оба :-)

Можно ли вызвать IBAction в NSWindowController из NSObject? Или есть другой способ сделать это?

Я совсем новичок в Cocao и Objective-C, поэтому будьте осторожны :-)


person Joran    schedule 10.06.2011    source источник


Ответы (2)


Если ваш лист представляет собой nib/xib с NSPanel, вызов для его закрытия будет просто [panel close]; Предполагая, что ваш оконный контроллер имеет свойство для панели, вы можете поместить код закрытия в конец IBAction, добавляющего строку. Или вы можете сделать так, чтобы сам IBAction вызывал другой метод, если хотите.

Если ваша панель работает в модальном режиме, вам также может понадобиться stopModal. (Это то, что нужно, если все остается замороженным после закрытия панели; в противном случае не имеет значения.)

Предполагая, что hopBill, ваш источник данных, является свойством оконного контроллера, любой IBAction, который вы пишете в оконном контроллере, также имеет доступ к hopBill; он может делать все, что вам нужно.

Поэтому добавьте один IBAction в оконный контроллер и подключите к нему кнопку OK на панели. Это должно сработать.

Что касается вызова IBAction откуда-то, кроме элемента управления в перо, да, вы можете это сделать. Используйте ссылку на элемент управления в качестве аргумента отправителя или nil, если IBAction не использует аргумент отправителя.

Вы также можете создать свою панель программно или использовать NSAlert. Но похоже, что ваша текущая установка проще и, следовательно, лучше.


Взгляните на этот файл h для контроллера приложения: Apple ClockControl пример

Свойство NSMutableArray *appointments — это фактический источник данных, который будет использоваться методами протокола NSTableViewDataSource. IBAction «addAppointment» может напрямую обращаться к «назначениям»: [self.appointments addObject: что угодно atIndex: что угодно];

Пример ClockControl можно изменить, чтобы использовать HopBill. Вы должны импортировать его объявления вверху: #import "HopBill.h" И затем вместо свойства "встречи" он должен объявить HopBill *hopBill; И "addApointment" будет обращаться к изменяемому массиву HopBill (aHopBill) следующим образом: [self.hopBill.aHopBill addObject:что угодно atIndex:что угодно];

person Wienke    schedule 10.06.2011
comment
Спасибо за Ваш ответ. NSPanel работает как модальный и является другим объектом, чем мой источник данных hopBill. Вот почему у меня нет доступа к hopBill. Мне нужно выполнить два действия над двумя разными объектами, добавив запись в мой источник данных и закрыв окно. Или лучше всего сделать один объект из объекта источника данных и объекта модального окна? - person Joran; 11.06.2011
comment
Правильно, NSPanel должен быть объектом, отличным от источника данных. Как панель, так и HopBill обычно являются свойствами вашего оконного контроллера. Я добавил к своему ответу пример, который может сделать структуру более понятной. Вы также можете обратиться к книге по программированию на Objective-C или Cocoa для получения полных руководств. Трудно понять этот материал только путем обсуждения. - person Wienke; 11.06.2011
comment
Спасибо за Ваш ответ. Кажется, я не могу заставить его работать. Я добавил #import HopBill.h в HopBillSheetController.h. И вставил указанный вами код в HopBillSheetController.m. Но при сборке я получаю сообщение об ошибке в строке [self.hopBill.aHopBill] с указанием запроса на членство 'hopBill' в чем-то, что не является структурой или объединением. Возможно, я делаю что-то совершенно неправильно, поэтому я загрузил исходный код на: kloran.home.xs4all .nl/iBrouwsrc.zip. У меня есть книги Objective-C для чайников и Cocoa Programming for Mac OS X для чайников. - person Joran; 11.06.2011
comment
XIB, содержащий ваше главное окно и панель, имеет владельца файла типа MyDocument, который является подклассом NSDocument. Я никогда не работал с NSDocument, который, судя по «Обзору приложений, основанных на документах» Apple, имеет особую архитектуру. Так что тут я мало чем могу помочь — хотя я подозреваю, что вам придется переместить свои IBActions в то, что станет вашим контроллером верхнего уровня, возможно, в MyDocument. И MyDocument, вероятно, должен иметь свойство, которое является экземпляром HopBillSheetController. Синтаксис с точкой, как и в self.hopBill.aHopBill, не будет работать без строк @property и @synthesize. - person Wienke; 11.06.2011
comment
Извинения: я не заметил, что ваш вопрос начинается с приложения на основе моего документа... Это должно было указать мне на тот факт, что структура вашего приложения отличается от того, к чему я привык. (Я добавил тег NSDocument к вашему вопросу. Это может помочь лучше.) - person Wienke; 11.06.2011
comment
Я прочитал немного больше о шаблоне проектирования MVC, и что меня беспокоит, так это то, что объект HopBill, содержащий массив для табличного представления, не создается кодом (HopBill *aHopBill = [HopBill new];), а создается в другой путь. Я не могу просто отправлять ему сообщения. Вы никогда не создаете свой собственный объект источника данных в коде для tableView? - person Joran; 12.06.2011
comment
Кажется, я вижу основную проблему. Объяснение длинное, поэтому я добавляю еще один ответ. - person Wienke; 12.06.2011

Почему вы не можете отправлять сообщения на hopBill:

Во-первых, потому что, хотя вы объявляете его, вы никогда его не инициализируете. У тебя есть:

HopBill *hopBill;
[self.hopBill.aHopBill addObject: bHopAdditionAtInit];

Так должно быть:

HopBill *hopBill = [[HopBill alloc] init];
[hopBill.aHopBill addObject: bHopAdditionAtInit]; // “self” won’t work here

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

В интерфейсе HopBill вы объявляете массив aHopBill как свойство и инициализируете его в методе init HopBill (вы также должны освободить его в методе Dealloc HopBill). Вам нужно сделать то же самое для контроллера — он должен иметь экземпляр HopBill в качестве свойства, и этот экземпляр должен быть инициализирован в методе инициализации контроллера.

Если вы хотите, чтобы HopBillController управлял табличным представлением, объявление его интерфейса должно выглядеть так:

@interface HopBillSheetController : NSWindowController <NSTableViewDelegate, NSTableViewDataSource> {
…
}

И затем, конечно, вы должны реализовать соответствующие методы NSTableViewDelegate и NSTableViewDataSource.

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

[self.tableview setDelegate:self];
[self.tableview setDataSource:self];

(Синтаксис с точками предполагает, что вы настроили код @property и @synthesize для табличного представления.)

Метод IBAction, который добавляет элементы в вашу таблицу, должен находиться в этом классе контроллера или в классе, у которого есть свойство, являющееся экземпляром класса контроллера. Тогда метод IBAction получит доступ к массиву aHopBill и сможет добавить в массив новый объект, после чего вызовет [tableView reloadData], что, в свою очередь, вызовет методы протокола tableview и обновит таблицу.

Теперь это означает, что xib, содержащий табличное представление, должен иметь контроллер в качестве владельца файла. Поскольку вы используете NSDocument, я подозреваю, что вместо этого вы поместите вывод tableview в подкласс NSDocument. И вы бы дали этому подклассу документа свойство, которое является экземпляром контроллера. Методы IBAction также будут в подклассе doc, поэтому у них будет доступ к контроллеру и его свойству HopBill. Или, может быть, вы просто сделаете подкласс doc контроллером, вместо того, чтобы использовать отдельный класс HopBillSheetController. Я не уверен насчет NSDocument. Но помните, метод IBAction может сам вызывать другие методы, если он имеет доступ к экземплярам классов, в которых объявлены эти методы.

У Apple есть пример, использующий методы делегата tableview и протокола источника данных. Перейдите по этой ссылке и загрузите пример кода: пример tableview

Похоже, хорошее приложение. Удачи.

person Wienke    schedule 12.06.2011