Сохранение закрытия блока как IBAction - PullRequest
2 голосов
/ 18 сентября 2010

Я пытаюсь уменьшить церемонию и из академического любопытства хочу узнать, как сделать следующее без метода IBAction, определенного в файле .m, чтобы использовать замыкание всякий раз, когда происходит проводное действие Interface Builder, например нажатие кнопки. Вы могли бы сказать, что я хочу подразумевать метод cancelButtonPress ниже вместо того, чтобы определять его. Подкласс UIViewController или некоторая магия, хранящаяся в категории, вполне приемлема.

@interface MyViewController : UIViewController
{
    void(^doOnCancel)(void);
}

@property (nonatomic, copy) void(^doOnCancel)(void);

- (IBAction)cancelButtonPress:(id)sender;//I want this gone!

@end

Я попытался изменить void на IBAction в свойстве и переменной безуспешно.

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

Награда за хороший шаблон, который позволит произвольно использовать замыкания для обслуживания действий, определенных в IB, таким образом, чтобы их можно было использовать для сокращения церемоний. Пока что комментарии "не могу этого сделать" могут быть или не быть правильными.

Ответы [ 3 ]

4 голосов
/ 22 сентября 2010

Подход Арумана хорош, но он, вероятно, слишком сильно зависит от времени выполнения. Помимо проблем с производительностью, Swizzling и использование initialize более восприимчивы к ошибкам, если кто-то еще хочет поиграть со временем выполнения одновременно. Я предлагаю использовать макросы C.

Определите эти макросы в верхней части вашего класса (или где угодно, на самом деле):

typedef void(^BlockAction)(id sender);
#define BlockActionProperty(ACTION)     @property (copy) BlockAction ACTION;\
                                        - (IBAction) ACTION:(id)sender;

#define BlockActionSynthesize(ACTION)   @synthesize ACTION;\
                                        - (IBAction) ACTION:(id)sender {\
                                            if (ACTION) ACTION(sender);\
                                        }

Теперь, чтобы создать новое действие, все, что вам нужно сделать, это заменить @property ... в заголовке следующим (например):

BlockActionProperty(testAction);

и "синтезировать" его в реализации:

BlockActionSynthesize(testAction);

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

Это быстрее (и, на мой взгляд, чище), чем делать это во время выполнения, и, поскольку IBAction определен, Interface Builder может "увидеть" его определение.

3 голосов
/ 21 сентября 2010

Итак, хак, позволяющий использовать любое свойство блочного типа в качестве действия: http://gist.github.com/589740

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

Кроме того, оно не полностью соответствует вашим требованиям, поскольку действие объявлено в невыполненной категории. Это сделано исключительно для того, чтобы Interface Builder мог автоматически находить его путем сканирования заголовков. Вы можете удалить это и добавить действие вручную в инспекторе IB, но мне кажется, что это потеря для меня, поскольку правильно поддерживать его синхронизацию сложнее.

1 голос
/ 18 сентября 2010

Не прибегая к чему-то вроде «Поместите код, который обычно будет в cancelButtonPress: в forwardInvocation:», вы не сможете. Действия Interface Builder отправляют сообщения. Вызов блока не может быть прямым результатом сообщения действия.

Можно расширить элемент управления, чтобы он вызывал блок вместо отправки обычного метода действия вашему контроллеру, но для этого потребовалось бы гораздо больше кода, и это волшебным образом не заставило бы Interface Builder его поддерживать.

...