пятница, 24 октября 2014 г.

Использование ассоциированных объектов на примере UIAlertView и UIActionSheet

Существуют ситуации, в которых вам необходимо отображать более одного AlertView или ActionSheet и, более того, обрабатывать нажатие кнопки. При этом мы наблюдаем следующее - код вызова данных элементов отделен от кода обработки. В таком случае логично определить для каждого вызываемого объекта идентификатор (свойство tag) и направлять ответ пользователя на обработку тому или иному методу. В результате мы получим нечто вроде этого:
#pragma mark - Calling Alerts
- (IBAction)btn_callAlertOne:(id)sender {
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"First Alert" message:@"Alert message" delegate:self cancelButtonTitle:@"OK" otherButtonTitles:@"Boom",nil];
    alert.tag = 1;
    [alert show];
}

- (IBAction)btn_callAlertTwo:(id)sender {
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Second Alert" message:@"Alert message" delegate:self cancelButtonTitle:@"OK" otherButtonTitles:@"Boom",nil];
    alert.tag = 2;
    [alert show];
}

- (IBAction)btn_callAlertThree:(id)sender {
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Third Alert" message:@"Alert message" delegate:self cancelButtonTitle:@"OK" otherButtonTitles:@"Boom",nil];
    alert.tag = 3;
    [alert show];
}

- (IBAction)btn_callAlertFour:(id)sender {
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Fourth Alert" message:@"Alert message" delegate:self cancelButtonTitle:@"OK" otherButtonTitles:@"Boom",nil];
    alert.tag = 4;
    [alert show];
}

#pragma mark - UIAlertView Delegate
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
    switch (alertView.tag) {
        case 1:
            [self alertView_firstAlertClickedButtonAtIndex:buttonIndex];
            break;
        case 2:
            [self alertView_secondAlertClickedButtonAtIndex:buttonIndex];
            break;
        case 3:
            [self alertView_thirdAlertClickedButtonAtIndex:buttonIndex];
            break;
        case 4:
            [self alertView_fourthAlertClickedButtonAtIndex:buttonIndex];
            break;
    }
}

- (void)alertView_firstAlertClickedButtonAtIndex:(NSInteger)buttonIndex {
    switch (buttonIndex) {
        case 0:
            break;
        case 1:
            break;
    }
}

- (void)alertView_secondAlertClickedButtonAtIndex:(NSInteger)buttonIndex {
    switch (buttonIndex) {
        case 0:
            break;
        case 1:
            break;
    }
}

- (void)alertView_thirdAlertClickedButtonAtIndex:(NSInteger)buttonIndex {
    switch (buttonIndex) {
        case 0:
            break;
        case 1:
            break;
    }
}

- (void)alertView_fourthAlertClickedButtonAtIndex:(NSInteger)buttonIndex {
    switch (buttonIndex) {
        case 0:
            break;
        case 1:
            break;
    }
}
Даже с отсутствующими алгоритмами обработки нажатий данный код выглядит массивно. Для каждого сообщения (alert или actionsheet) определяется метод вызова и метод обработки, плюс общий метод сортировки. Упростить данную структуру можно, если описать обработку нажатий при создании сигнала. Для этого необходимо ассоциировать блок обработки с сигналом. Сделать это можно с помощью функции objc_setAssociatedObject, доступной в runtime. Взглянем на описание функции:
/** 
 * Sets an associated value for a given object using a given key and association policy.
 * 
 * @param object The source object for the association.
 * @param key The key for the association.
 * @param value The value to associate with the key key for object. Pass nil to clear an existing association.
 * @param policy The policy for the association. For possible values, see “Associative Object Behaviors.”
 * 
 * @see objc_setAssociatedObject
 * @see objc_removeAssociatedObjects
 */
OBJC_EXPORT void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy);
Воспользуемся ассоциированным объектом. Назначим блок обработки в качестве значения (value). Импортируем заголовок runtime и добавим ключ.
// Импорт
#import 
// Определим ключ
static void *ALMyAlertViewKey = "ALMyAlertViewKey";

- (IBAction)btn_callAlertOne:(id)sender {
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"First Alert" message:@"Alert message" delegate:self cancelButtonTitle:@"OK" otherButtonTitles:@"Boom",nil];
    
    objc_setAssociatedObject(alert, ALMyAlertViewKey, ^(NSInteger buttonIndex){
        switch (buttonIndex) {
            case 0:
                break;
            case 1:
                break;
        }
    }, OBJC_ASSOCIATION_COPY);
    
    [alert show];
}

- (IBAction)btn_callAlertTwo:(id)sender {
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Second Alert" message:@"Alert message" delegate:self cancelButtonTitle:@"OK" otherButtonTitles:@"Boom",nil];
    objc_setAssociatedObject(alert, ALMyAlertViewKey, ^(NSInteger buttonIndex){
        switch (buttonIndex) {
            case 0:
                break;
            case 1:
                break;
        }
    }, OBJC_ASSOCIATION_COPY);
    [alert show];
}

- (IBAction)btn_callAlertThree:(id)sender {
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Third Alert" message:@"Alert message" delegate:self cancelButtonTitle:@"OK" otherButtonTitles:@"Boom",nil];
    objc_setAssociatedObject(alert, ALMyAlertViewKey, ^(NSInteger buttonIndex){
        switch (buttonIndex) {
            case 0:
                break;
            case 1:
                break;
        }
    }, OBJC_ASSOCIATION_COPY);
    [alert show];
}

- (IBAction)btn_callAlertFour:(id)sender {
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Fourth Alert" message:@"Alert message" delegate:self cancelButtonTitle:@"OK" otherButtonTitles:@"Boom",nil];
    objc_setAssociatedObject(alert, ALMyAlertViewKey, ^(NSInteger buttonIndex){
        switch (buttonIndex) {
            case 0:
                break;
            case 1:
                break;
        }
    }, OBJC_ASSOCIATION_COPY);
    [alert show];
}

#pragma mark - UIAlertView Delegate
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
    void (^associatedBlock)(NSInteger) = objc_getAssociatedObject(alertView, ALMyAlertViewKey);
    associatedBlock(buttonIndex);
}
Девять методов сократились до пяти. Теперь код создания сигнального окна и обработки результата находятся в одном месте. Главный обработчик -alertView:clickedButtonAtIndex: теперь содержит две строки: первая получает ассоциированный объект (value), а именно наш блок, а вторая передает индекс нажатой кнопки данному блоку. Для ActionSheet методы выглядят следующим образом:
// Импорт
#import 
// Определим ключ
static void *ALMyActionSheetKey = "ALMyActionSheetKey";

- (IBAction)btn_callActivitySheetOne:(id)sender {
    NSString *actionSheetTitle = @"1st Action Sheet title";
    NSString *destructiveButtonTitle = @"Red button";
    NSString *cancelTitle = @"Cancel";
    
    UIActionSheet *actionSheet = [[UIActionSheet alloc]
                                  initWithTitle:actionSheetTitle
                                  delegate:self
                                  cancelButtonTitle:cancelTitle
                                  destructiveButtonTitle:destructiveButtonTitle
                                  otherButtonTitles:nil];
    objc_setAssociatedObject(actionSheet, ALMyActionSheetKey, ^(NSInteger buttonIndex){
        switch (buttonIndex) {
            case 0:
                break;
            case 1:
                break;
        }
    }, OBJC_ASSOCIATION_COPY);
    [actionSheet showInView:self.view];
}

- (IBAction)btn_callActivitySheetTwo:(id)sender {
    NSString *actionSheetTitle = @"2nd Action Sheet title";
    NSString *destructiveButtonTitle = @"Red button";
    NSString *cancelTitle = @"Cancel";
        
    UIActionSheet *actionSheet = [[UIActionSheet alloc]
                                      initWithTitle:actionSheetTitle
                                      delegate:self
                                      cancelButtonTitle:cancelTitle
                                      destructiveButtonTitle:destructiveButtonTitle
                                      otherButtonTitles:nil];
    objc_setAssociatedObject(actionSheet, ALMyActionSheetKey, ^(NSInteger buttonIndex){
        switch (buttonIndex) {
            case 0:
                break;
            case 1:
                break;
        }
    }, OBJC_ASSOCIATION_COPY);
    [actionSheet showInView:self.view];
}

- (IBAction)btn_callActivitySheetThree:(id)sender {
    NSString *actionSheetTitle = @"3rd Action Sheet title";
    NSString *destructiveButtonTitle = @"Red button";
    NSString *cancelTitle = @"Cancel";
    
    UIActionSheet *actionSheet = [[UIActionSheet alloc]
                                  initWithTitle:actionSheetTitle
                                  delegate:self
                                  cancelButtonTitle:cancelTitle
                                  destructiveButtonTitle:destructiveButtonTitle
                                  otherButtonTitles:nil];
    objc_setAssociatedObject(actionSheet, ALMyActionSheetKey, ^(NSInteger buttonIndex){
        switch (buttonIndex) {
            case 0:
                break;
            case 1:
                break;
        }
    }, OBJC_ASSOCIATION_COPY);
    [actionSheet showInView:self.view];
}

- (IBAction)btn_callActivitySheetFour:(id)sender {
    NSString *actionSheetTitle = @"4th Action Sheet title";
    NSString *destructiveButtonTitle = @"Red button";
    NSString *cancelTitle = @"Cancel";
    
    UIActionSheet *actionSheet = [[UIActionSheet alloc]
                                  initWithTitle:actionSheetTitle
                                  delegate:self
                                  cancelButtonTitle:cancelTitle
                                  destructiveButtonTitle:destructiveButtonTitle
                                  otherButtonTitles:nil];
    objc_setAssociatedObject(actionSheet, ALMyActionSheetKey, ^(NSInteger buttonIndex){
        switch (buttonIndex) {
            case 0:
                break;
            case 1:
                break;
        }
    }, OBJC_ASSOCIATION_COPY);
    [actionSheet showInView:self.view];
}

#pragma mark - UIActionSheet Delegate
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
    void (^associatedBlock)(NSInteger) = objc_getAssociatedObject(actionSheet, ALMyActionSheetKey);
    associatedBlock(buttonIndex);
}

Комментариев нет:

Отправить комментарий