#objective-c #uiview #uialertview #alert
#objective-c #uiview #uialertview #предупреждение
Вопрос:
У меня есть несколько UIAlertViews, которые я хочу отображать в последовательном порядке, и переходить к отображению следующего UIAlertView только после того, как предыдущий был отклонен (пользователь нажимает ok).
Я знаю о делегате didDismissWithButtonIndex и добавлении тега, но на самом деле это не слишком помогает, поскольку может быть вызвано до 3 UIAlertViews и не обязательно в одном и том же порядке каждый раз. посмотреть код:
if(condition 1){
alert1 = // UIAlertView[[.....
[alert1 show]
}
if(condition 2){
alert2 = // UIAlertView[[.....
[alert2 show]
}
if(condition 3){
alert3 = // UIAlertView[[.....
[alert3 show]
}
Вышесказанное просто добавит 3 предупреждения друг над другом (в зависимости от того, сколько условий выполнено), чего я не хочу. Я хочу иметь возможность показывать только один просмотр за раз, а затем следующий (если он есть) после того, как пользователь нажмет кнопку ok.
У меня была идея, возможно, добавить сообщения в очередь, а затем обработать эту очередь, удаляя предупреждение каждый раз, когда предупреждение отклоняется, но я не уверен, как я собираюсь это делать.
Любые идеи были бы высоко оценены. Спасибо
Ответ №1:
Вы могли бы легко сделать это в методе делегирования UIAlertView, вызываемом после отклонения представления предупреждения. Итак, UIAlertViewDelegate определяет следующий метод делегирования:
– alertView:didDismissWithButtonIndex:
Реализуйте этот метод и убедитесь, что ваш класс является делегатом создаваемых вами UIAlertViews. Этот метод является идеальным местом для отображения следующего предупреждения на основе того, которое было отклонено пользователем.
Если вашим требованием является «Отображать до трех предупреждений последовательно, но не всегда в одном и том же порядке», я бы, вероятно, поместил предупреждения в массив, а затем в методе делегирования извлеките следующее предупреждение из массива для отображения. На самом деле это не должно быть сложнее, чем это; ключевым моментом является то, что реализация метода делегирования является лучшим местом для отображения следующего предупреждения.
Пример псевдокода:
Определить массив; NSMutableArray * оповещения_;
- (void)showAlertSequence {
if ( !alerts_ ) {
alerts_ = [[NSMutableArray alloc] init];
}
[alerts_ addObjects;<My alerts>];
[self showSequencedAlertFrom:nil];
}
- (BOOL)showSequencedAlertFrom:(UIAlertView *)sourceAlertView {
if ( !sourceAlertView ) {
[[alerts_ objectAtIndex:0] show];
}
else {
NSInteger index = [alerts_ indexOfObject:sourceAlertView];
if ( index < [alerts_ count] ) {
[[alerts_ objectAtIndex:index ] show];
}
}
return NO;
}
– alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)index {
// Show the next alert or clean up if we're at the end of the sequence.
if ( ![self showSequencedAlertFrom:alertView] ) {
[alerts_ removeAllObjects];
}
}
Кстати, три последовательных предупреждения действительно будут раздражать ваших пользователей 😉
Комментарии:
1. спасибо @dannywartnaby Это похоже на то, что я уже пробовал. Проблема в том, что здесь вы предполагаете, что все alertViews существуют. Что, если существуют только AlertView 1 и 3? Ваш псевдокод пропустил бы alert3, если бы это было так. Или я упускаю что-то очевидное?
2. Другая проблема с этим подходом заключается в том, что предупреждение 1 не всегда существует, что означает, что ни одно из других предупреждений никогда не будет отображаться. Вот почему я рассматривал возможность использования другого подхода к методу делегирования. Спасибо за ваш ответ
3. Хорошо, я понял. Что ж, я немного обновил свой ответ; суть в том, что метод делегирования, ИМО, является лучшим местом для принятия решения о том, когда показывать следующее предупреждение. Если вы не можете последовательно знать, какие оповещения отображаются и в каком порядке, тогда имеет смысл IMO поместить оповещения, которые вы хотите отобразить, в коллекцию, а затем по существу выполнить итерацию по этой коллекции в методе делегирования, получая «следующий» на основе текущего.
4. Еще раз спасибо! Это кажется гораздо более логичным. Я пытался реализовать это, однако, когда я отклоняю первое оповещение, консоль возвращает сообщение: wait_fences: не удалось получить ответ: 10004003. Приложение останавливается. Я не вижу ничего очевидного, что могло бы быть причиной этого .. еще раз спасибо за вашу помощь, я думаю, что я почти на месте!
Ответ №2:
Одна вещь, которую я сделал, это использовал UIAlertViews на основе блоков, добавив категорию в AlertView.
Вот файл .h
@interface UIAlertView (WithBlocks)
- (id) initWithTitle:(NSString *)title message:(NSString *)message;
- (void) addButtonWithTitle:(NSString *)title andBlock:(void(^)())block;
@end
Вот файл .m
static NSString *BUTTON_BLOCK_KEY = @"alertview-button-blocks";
@interface UIAlertView()
- (void) runBlock: (void (^)())block;
@end
@implementation UIAlertView (WithBlocks)
/**
* Initialized an alert view with a title and message.
*/
- (id) initWithTitle:(NSString *)title message:(NSString *)message
{
self = [self initWithTitle:title message:message delegate:nil cancelButtonTitle:nil otherButtonTitles:nil];
if (self) {
self.delegate = self;
NSMutableArray *buttonBlocks = [NSMutableArray array];
objc_setAssociatedObject(self, BUTTON_BLOCK_KEY, buttonBlocks, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
return self;
}
/**
* Adds a button with a title and a block to be executed when that button is tapped.
*/
- (void) addButtonWithTitle:(NSString *)title andBlock:(void (^)())block
{
// Add the button
[self addButtonWithTitle:title];
NSMutableArray *buttonBlocks = objc_getAssociatedObject(self, BUTTON_BLOCK_KEY);
if (!block) {
block = ^{ /* empty block */ };
}
[buttonBlocks addObject:[[[block copy] retain] autorelease]];
}
- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex
{
NSMutableArray *buttonBlocks = objc_getAssociatedObject(self, BUTTON_BLOCK_KEY);
void (^block)() = (void (^)()) [buttonBlocks objectAtIndex:buttonIndex];
// Due to a timing issue, the current window is still the UIAlertView for a very
// short amount of time after it has been dismissed which messes up anything
// trying to get the current window in the blocks being run.
// Ergo, the block is being delayed by a tiny bit. (Amount determined through limited testing)
[self performSelector:@selector(runBlock:) withObject:block afterDelay:0.25];
}
- (void) runBlock: (void (^)())block
{
block();
}
@end
Затем вы можете вызвать цепочку alertviews вместе с помощью следующего кода
void(^continueBlock)(void) = ^{
// Display more alertviews here
};
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Title" message:@"message"];
[alert addButtonWithTitle:@"Continue" andBlock:continueBlock];
[alert addButtonWithTitle:@"Dismiss" andBlock:^{
// Display more alertviews here
}
[alert show];
[alert release];
Ответ №3:
Я также искал решение этой проблемы. Вот как я в конечном итоге решил это для своего собственного приложения:
static BOOL alertShowing = FALSE;
UIAlertView *alert0 = [[UIAlertView alloc] initWithTitle:@"AlertView 0" message:@"This is the first alert" delegate:self cancelButtonTitle:nil otherButtonTitles:@"Yes",@"No", nil];
[alert0 setTag:0];
alertShowing = TRUE;
[alert0 show];
while (alertShowing) {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.2]];
}
UIAlertView *alert1 = [[UIAlertView alloc] initWithTitle:@"AlertView 1" message:@"This is the second alert" delegate:self cancelButtonTitle:nil otherButtonTitles:@"Yes",@"No", nil];
[alert1 setTag:1];
alertShowing = TRUE;
[alert1 show];
while (alertShowing) {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.2]];
}
// add some more alerts here for dramatic effect ...
Ваш обработчик кнопок должен быть установлен alertShowing = FALSE'
на каждом пути выхода.
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
// Deal with handling responses for your different alerts here.
switch ([alertView tag]) {
case 0:
// handler first alert here
break;
case 1:
// handler second alert here
break;
default:
// etc.
break;
}
alertShowing = FALSE;
}
Могут быть лучшие способы сидеть и вращаться, чем создание нового цикла выполнения, и есть некоторый дублирующий код, который, вероятно, можно было бы обобщить лучше. С положительной стороны, это просто и не требует кучи логики очередей. Я использую #define для этого шаблона, чтобы избежать необходимости вводить его вручную, и в моем случае это отлично сработало.
Ответ №4:
Вот как я это сделал, используя очередь оповещений, как вы предложили.
@property (strong, nonatomic) NSMutableArray *alertQueue;
@property (nonatomic) BOOL showingAlert;
- (void)showAlert:(UIAlertView *)alert {
if (self.showingAlert) {
[self.alertQueue addObject:alert];
}
else {
self.showingAlert = YES;
[alert show];
}
}
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if ([self.alertQueue count]) {
UIAlertView *alert = [self.alertQueue objectAtIndex:0];
[self.alertQueue removeObjectAtIndex:0];
[alert show];
}
else self.showingAlert = NO;
}
Затем всякий раз, когда вы хотите отобразить предупреждение, вы просто создаете UIAlertView и передаете его методу showAlert, и он будет отображаться только после того, как все предыдущие предупреждения будут отклонены.