3

在 iphone 应用程序中使用 UIActionSheet 时,将动作与按钮匹配的典型方法看起来非常脆弱且不美观。可能是因为我的 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 个选项来说,这感觉非常笨拙和麻烦。我还考虑过使用枚举来掩盖索引的魔力,但这只是问题的一小部分。

真正的问题似乎是没有(简单?)方法可以在一个地方同时指定按钮字符串列表和相应的操作,从而在添加/删除/重新排序选项时无需在两个地方修改代码,从而使其有效不可能犯我的示例代码所犯的那种错误。

我并不是要开始一场编程语言的圣战,我只是想弄清楚在这种情况下(我相信 Objective C 中的许多其他人)将按钮字符串列表连接到动作列表的正确设计模式是什么。

4

5 回答 5

4

我更喜欢这种方式

- (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"])
    {
        //...
    }
}
于 2010-10-20T04:04:37.683 回答
3

我完全同意这个问题。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
    }
}
于 2015-03-26T06:12:12.030 回答
1

也许你可以把按钮的动作放在一个数组中

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]];
    }
}

我很确定您可以在数组中放置一个更复杂的对象,包括按钮信息和方法,然后将其全部包含在数组中。可能对数组的索引进行更好的错误检查....等

老实说,在我读到这个问题之前,我从来没有想过这种模式,所以这只是我的想法

于 2010-10-20T03:27:48.020 回答
1

那个怎么样?

这样,您就不必担心索引,因为按钮和操作被添加在同一个地方。

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;
}
于 2012-05-25T00:52:12.077 回答
-1

基于 Aaron 的选择器建议,我现在真的很喜欢做一个简单的临时调度方法的想法。它成功地避免了处理错误选项的可能性,并提供了一个清晰的关注点分解。当然,我可以想象一个用例,您希望首先为每个选项执行其他操作,例如实例化一个对象并将选项字符串传递给它,就像 Toro 的回答一样。

这是一个简单的调度,它调用诸如“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")
    }
}
于 2010-10-20T05:26:18.423 回答