Правильный способ подключения параметров UIActionSheet к действиям - PullRequest
3 голосов
/ 20 октября 2010

При использовании UIActionSheet в приложении для iPhone типичные методы сопоставления действий с кнопками кажутся очень хрупкими и эстетически неприятными. Возможно, это из-за моего минимального опыта в C / C ++ (больше Perl, Java, Lisp и других). Совпадение по индексам кнопок кажется слишком большим количеством магических чисел и слишком отсоединено, чтобы избежать простых логических ошибок или ошибок согласованности.

Например,

UIActionSheet *sources = [[UIActionSheet alloc]
         initWithTitle:@"Social Networks"
              delegate:self 
     cancelButtonTitle:@"Cancel" 
destructiveButtonTitle:nil 
     otherButtonTitles:@"Twitter", @"Facebook", @"Myspace", @"LinkedIn", @"BlahBlah", nil
];

<snip>

-(void)actionSheet:(UIActionSheet *)actionSheet didDismissWithButtonIndex:(NSInteger)buttonIndex {
    if (buttonIndex == [actionSheet cancelButtonIndex]) {
        // all done
    } else if (buttonIndex == 0) {
        // Twitter
    } else if (buttonIndex == 1) {
        // Facebook
    } else if (buttonIndex == 2) {
        // LinkedIn
    } else if (buttonIndex == 3) {
        // Myspace
    }
}

Обратите внимание, что в коде обработки действий есть как минимум две ошибки (по крайней мере, согласно комментариям).

Чего мне не хватает, так это правильного шаблона проектирования, позволяющего избежать отключения в Objective-C. Если бы это был Perl, я сначала создал бы массив параметров моей кнопки, а затем, вероятно, создал бы хеш таблицы быстрого поиска, который бы соответствовал другой таблице поиска объектов или подпрограмм, которые выполняли соответствующие действия для каждого элемента. В java исходный список, вероятно, будет в первую очередь объектами с обратными вызовами. Я знаю, что мог бы создать словарь для имитации хэша perl, но это кажется очень громоздким и громоздким для 3-4 вариантов. Я также подумал об использовании enum для маскировки волшебства индексов, но это лишь малая часть проблемы.

Реальная проблема заключается в том, что нет (простого?) Способа указать ОБА список строк кнопок и соответствующих действий в одном месте, тем самым устраняя необходимость в изменении кода в двух местах при добавлении / удалении / переупорядочении параметров и, таким образом, фактически исключая ошибки, которые допускает мой пример кода.

Я не пытаюсь начать священную войну языка программирования, я просто хочу выяснить, каков правильный шаблон проектирования в этом сценарии (и я верю многим другим в Задаче C) для соединения списка строк кнопок со списком действия.

Ответы [ 5 ]

4 голосов
/ 20 октября 2010

Я предпочитаю этот способ

- (void)actionSheet:(UIActionSheet *)actionSheet didDismissWithButtonIndex:(NSInteger)buttonIndex {
    if (buttonIndex == [actionSheet cancelButtonIndex]) 
    {
       // cancelled, nothing happen
       return;
    }

    // obtain a human-readable option string
    NSString *option = [actionSheet buttonTitleAtIndex:buttonIndex];
    if ([option isEqualToString:@"Twitter"])
    {
        //...
    } else if ([option isEqualToString:@"FaceBook"])
    {
        //...
    }
}
3 голосов
/ 26 марта 2015

Я полностью согласен с вопросом. Дизайн Apple поощряет использование магических чисел, и я был немного шокирован, увидев все решения, рекомендующие использовать жестко закодированные числа для индексов кнопок.

Вот мое решение для Swift.

  • Создать перечисление, содержащее элемент для каждого заголовка кнопки, например:
enum ImagePickerActionSheetButtons
{
    case Camera
    case Chooser
}

Заполните словарь ЛОКАЛИЗОВАННЫМИ строками для каждого заголовка кнопки, где ключи - это элементы из перечисления:

// Populate with LOCALIZED STRINGS
var buttonTitles:[ImagePickerActionSheetButtons:String] =
[ImagePickerActionSheetButtons.Camera:"Take photo",
    ImagePickerActionSheetButtons.Chooser :"Choose photo"]

Создать лист действий, получая заголовки кнопок из словаря по значению их перечисления:

func createActionSheet()->UIActionSheet
{
    var sheet: UIActionSheet = UIActionSheet()

    sheet.addButtonWithTitle(buttonTitles[.Camera]!)
    sheet.addButtonWithTitle(buttonTitles[.Chooser]!)

    sheet.addButtonWithTitle("Cancel")
    sheet.cancelButtonIndex = sheet.numberOfButtons - 1
    sheet.delegate = self
    return sheet
}

Наконец, в коде clickedButtonAtIndex проверьте заголовок нажатой кнопки на соответствие локализованным строкам в словаре:

func actionSheet(sheet: UIActionSheet!, clickedButtonAtIndex buttonIndex: Int)
{
    if (sheet.buttonTitleAtIndex(buttonIndex) == buttonTitles[.Camera]!)
    {
        takePhoto()
    }
    else if (sheet.buttonTitleAtIndex(buttonIndex) == buttonTitles[.Chooser]!)
    {
        choosePicFromLibrary()
    }
    else if (buttonIndex == sheet.cancelButtonIndex)
    {
        // do nothing
    }
}
1 голос
/ 25 мая 2012

Как насчет этого?

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

typedef void (^contact_callback_t)(MyContactsController *controller);
 … 
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
     NSDictionary *contact = [myContacts objectAtIndex:indexPath.row];     
     UIActionSheet *_actionSheet = [[UIActionSheet alloc] initWithTitle:NSLocalizedString(@"Contact Action", @"")
                                                          delegate:self
                                                 cancelButtonTitle:nil
                                            destructiveButtonTitle:nil
                                                 otherButtonTitles:nil];

 _actions = [NSMutableArray new];
 if([contact objectForKey:@"private_email"] != nil) {
     [_actionSheet addButtonWithTitle:
      [NSString stringWithFormat:NSLocalizedString(@"E-Mail: %@", @""), [contact objectForKey:@"private_email"] ] ];
     contact_callback_t callback = ^(MyContactsController *controller) {
         [controller openEmail:contact];
     };
     [_actions addObject:callback];
 }
 if([contact objectForKey:@"private_telefon"] != nil) {
     [_actionSheet addButtonWithTitle: 
      [NSString stringWithFormat:NSLocalizedString(@"Phone: %@", @""), [contact objectForKey:@"private_telefon"] ]];
     contact_callback_t callback = ^(MyContactsController *controller) {
         [controller dial:[contact objectForKey:@"private_telefon"]];
     };
     [_actions addObject:callback];
   }
  [_actionSheet showFromTabBar:tabBar];     

}

- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex 
{
  if(buttonIndex == actionSheet.cancelButtonIndex)
{
}
else
{
      contact_callback_t callback = [_actions objectAtIndex:buttonIndex];
      callback(self);
   }
  _actions = nil;
}
1 голос
/ 20 октября 2010

Может быть, вы могли бы поместить действия для кнопок в массив

actionsArray = [NSMutableArray arrayWithObjects: @selector(btn1Clicked),    
                                    @selector(btn2Clicked), 
                                    @selector(btn3Clicked), 
                                    @selector(btn4Clicked), nil];

, затем в didDismissWthButtonIndex

-(void)actionSheet:(UIActionSheet *)actionSheet didDismissWithButtonIndex:(NSInteger)buttonIndex {
    if (buttonIndex == [actionSheet cancelButtonIndex]) {
        // all done
    } else {
       [this [actionsArray objectAtIndex: buttonIndex]];
    }
}

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

Если честно, я никогда не задумывался об этом паттерне, пока не прочитал вопрос, так что это просто не в моей голове

0 голосов
/ 20 октября 2010

Опираясь на предложение селектора Аарона, мне сейчас очень нравится идея сделать простой специальный метод отправки.Это успешно исключает возможность обработки неправильной опции, а также обеспечивает чистую факторизацию проблем.Конечно, я мог бы представить вариант использования, когда вы хотите сначала сделать что-то еще для каждой опции, например, создать экземпляр объекта и передать ему строку опции, очень похожую на ответ Торо.

Вот простая диспетчеризация, которая вызывает методытакие как 'actionTwitter':

-(void)actionSheet:(UIActionSheet *)actionSheet didDismissWithButtonIndex:(NSInteger)buttonIndex {  
    if (buttonIndex == [actionSheet cancelButtonIndex]) {
        return;
    }

    NSString *methodName = [@"action" stringByAppendingString:[actionSheet buttonTitleAtIndex:buttonIndex]];
    SEL actionMethod = NSSelectorFromString(methodName);
    if ([self respondsToSelector:actionMethod]) {
        [self performSelector:actionMethod];
    } else {
        NSLog(@"Not yet implemented")
    }
}
...