Как безопасно передать объект контекста в делегат UIAlertView? - PullRequest
15 голосов
/ 30 июня 2009

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

-(void) showAlert: (id) ctx {
    UIAlertView *baseAlert = [[UIAlertView alloc] 
                          initWithTitle: title
                          message: msg
                          delegate:self
                          cancelButtonTitle: cancelButtonTitle
                          otherButtonTitles: buttonTitle1, buttonTitle2, nil];
    //baseAlert.context = ctx;
    [baseAlert show];
    [baseAlert release];
}


- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex {
    if (buttonIndex == 1) {
        id context = ...;//alertView.context;
        [self performSelectorOnMainThread:@selector(xxx:) withObject: context waitUntilDone: NO];
    }
}

Есть ли способ передать объект в делегат как объект контекста? или, может быть, каким-то другим способом?

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

Ответы [ 8 ]

12 голосов
/ 01 июля 2009

Я все еще думаю, что хранить его локально - лучшее решение. Создайте локальную переменную класса NSMutableDictionary для хранения карты объектов контекста, сохраните контекст с UIAlertView в качестве ключа и контекст в качестве значения.

Затем, когда вызывается метод alert, просто загляните в словарь, чтобы увидеть, к какому объекту контекста относится. Если вы не хотите использовать весь объект Alert в качестве ключа, вы можете использовать только адрес объекта UIAlertView:

NSString *alertKey = [NSString stringWithFormat:@"%x", baseAlert];

Адрес должен быть постоянным на телефоне. Или вы можете пометить каждое предупреждение, как предлагал другой автор, и использовать этот тег для поиска контекста на карте.

Не забудьте очистить объект контекста, когда закончите!

10 голосов
/ 24 марта 2011

Полная реализация, которая позволяет передавать контекст:

@interface TDAlertView : UIAlertView
@property (nonatomic, strong) id context;
@end

@implementation TDAlertView
@end

И пример использования, обратите внимание, как мы предварительно приводим указатель:

@implementation SomeAlertViewDelegate
- (void)alertView:(TDAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex {
     NSLog(@"%@", [alertView context]);
}
@end
7 голосов
/ 30 июня 2009

вы также можете использовать свойство tag (так как это подкласс UIView). Это просто int, но может быть достаточно для вас.

3 голосов
/ 08 марта 2013

Вместо обсуждения значения «не поддерживает создание подклассов» я предоставлю лучший ответ. Я создал общую категорию contextInfo для своей работы пару месяцев назад. Я просто положил его на github: JLTContextInfo .

#import "objc/runtime.h"

@interface NSObject (JLTContextInfo)
- (NSMutableDictionary *)JLT_contextInfo;
@end
@implementation NSObject (JLTContextInfo)
- (NSMutableDictionary *)JLT_contextInfo
{
    static char key;
    NSMutableDictionary *contextInfo = objc_getAssociatedObject(self, &key);
    if (!contextInfo) {
        contextInfo = [NSMutableDictionary dictionary];
        objc_setAssociatedObject(self, &key, contextInfo, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
    return contextInfo;
}
@end

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

-(void) showAlert: (id) ctx {
    UIAlertView *baseAlert = [[UIAlertView alloc] 
                          initWithTitle: title
                          message: msg
                          delegate:self
                          cancelButtonTitle: cancelButtonTitle
                          otherButtonTitles: buttonTitle1, buttonTitle2, nil];
    [[baseAlert JLT_contextInfo] setObject:ctx forKey:@"ctx"];
    [baseAlert show];
    [baseAlert release];
}

- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex {
    if (buttonIndex == 1) {
        id context = [[alertView JLT_contextInfo] objectForKey:@"ctx"];
        [self performSelectorOnMainThread:@selector(xxx:) withObject: context waitUntilDone: NO];
    }
}
3 голосов
/ 24 апреля 2012

Создание подклассов UIAlertView не очень хорошая идея.

http://developer.apple.com/library/ios/#DOCUMENTATION/UIKit/Reference/UIAlertView_Class/UIAlertView/UIAlertView.html

Примечания по подклассам

Класс UIAlertView предназначен для использования как есть и не поддерживает создание подклассов. Иерархия представления для этого класса является закрытой и не должна быть изменена.

UIAlertView с пользовательским контекстом и [self autorelease] отвечает на этот вопрос по-другому.

1 голос
/ 06 сентября 2013

Из моего другого ответа , вот быстрое и чистое решение, которое использует преимущества связанных объектов. В моем другом ответе я упомянул, что вы можете даже заменить UIAlertView на NSObject и эффективно добавить свойство context к любому объекту:

#import <objc/runtime.h>

@interface UIAlertView (Private)
@property (nonatomic, strong) id context;
@end

@implementation UIAlertView (Private)
@dynamic context;
-(void)setContext:(id)context {
    objc_setAssociatedObject(self, @selector(context), context, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
-(id)context {
    return objc_getAssociatedObject(self, @selector(context));
}
@end

И тогда вы сможете сделать что-то вроде:

NSObject *myObject = [NSObject new];

UIAlertView *alertView = ...
alertView.context = myObject;

ВАЖНО: И не забывайте обнулять контекст в dealloc !!

1 голос
/ 08 августа 2012

Я сделал смесь между ответом Кендалла и использованием блоков в одном из моих классов контроллера базового представления. Теперь я могу использовать AlertView и ActionSheets с блоками, что значительно улучшает читабельность. Вот как я это сделал:

В .h моего ViewController я объявляю тип блока (необязательно, но рекомендуется)

typedef void (^AlertViewBlock)(UIAlertView*,NSInteger);

Также я объявляю изменяемый словарь, в котором будут храниться блоки для каждого предупреждения:

NSMutableDictionary* m_AlertViewContext;

В файле реализации я добавляю метод для создания AlertView и сохранения блока:

-(void)displayAlertViewWithTitle:(NSString *)title message:(NSString *)message cancelButtonTitle:(NSString *)cancelButtonTitle withBlock:(AlertViewBlock)execBlock otherButtonTitles:(NSArray *)otherButtonTitles
    {
        UIAlertView* alert = [[UIAlertView alloc] initWithTitle:title
                                                        message:message
                                                       delegate:self cancelButtonTitle:cancelButtonTitle otherButtonTitles: nil];
        for (NSString* otherButtonTitle in otherButtonTitles) {
            [alert addButtonWithTitle:otherButtonTitle];
        }
        AlertViewBlock blockCopy = Block_copy(execBlock);
        [m_AlertViewContext setObject:blockCopy forKey:[NSString stringWithFormat:@"%p",alert]];
        Block_release(blockCopy);
        [alert show];
        [alert release];
    }

Обратите внимание, что я получаю те же атрибуты, что и конструктор UIAlertView, но делегат (который будет самостоятельным). Также я получаю объект AlertViewBlock, который я сохраняю в изменяемом словаре m_AlertViewContext. Затем я показываю предупреждение, как обычно.

В обратных вызовах делегата я вызываю блок и задаю ему параметры:

#pragma mark -
#pragma mark UIAlertViewDelegate

- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
    NSString* blockKey = [NSString stringWithFormat:@"%p",alertView];
    AlertViewBlock block  = [m_AlertViewContext objectForKey:blockKey];
    block(alertView,buttonIndex);
    [m_AlertViewContext removeObjectForKey:blockKey];
}

- (void)alertViewCancel:(UIAlertView *)alertView {
    NSString* blockKey = [NSString stringWithFormat:@"%p",alertView];
    [m_AlertViewContext removeObjectForKey:blockKey];
}

Теперь, когда мне нужно использовать AlertView, я могу назвать его так:

[self displayAlertViewWithTitle:@"Title"
                                message:@"msg"
                      cancelButtonTitle:@"Cancel"
         withBlock:^(UIAlertView *alertView, NSInteger buttonIndex) {
             if ([[alertView buttonTitleAtIndex:buttonIndex] isEqualToString:@"DO ACTION"]){
                 [self doWhatYouHaveToDo];
             }
         } otherButtonTitles:[NSArray arrayWithObject:@"DO ACTION"]];

Я сделал то же самое для ActionSheet, и теперь их действительно легко использовать. Надеюсь, это поможет.

0 голосов
/ 30 июня 2009

Вы можете создать подкласс UIAlertView и добавить туда свойство.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...