2010-10-20 64 views
3

在iPhone应用程序中使用UIActionSheet时,将操作与按钮匹配的典型方法看起来非常脆弱并且美观令人不愉快。也许是由于我最小的C/C++背景(更多Perl,Java,Lisp等)。在按钮索引上进行匹配看起来像是太多的幻数,并且太不连贯以至于避免简单的逻辑或一致性错误。将UIActionSheet选项连接到操作的正确方法

例如,

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 hash,但是这对于3-4个选项来说感觉非常笨拙和麻烦。我也考虑过使用枚举来掩盖索引的魔力,但这只是问题的一小部分。

真正的问题似乎是没有(简单的?)的方式来指定一个位置的按钮字符串和相应的动作列表,从而消除了在添加/删除/重新排序选项时需要在两个地方修改代码和从而使得我的示例代码所犯的错误实际上不可能发生。我不想开始一个编程语言圣战,我只是想在这种情况下(和我相信在目标C中的许多其他人)将正确的设计模式连接到列表中的按钮字符串列表的行动。

回答

-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") 
    } 
} 
+0

你是天才。我不这样想。 – AechoLiu 2010-10-20 09:08:23

+2

这几乎可以确保您无法将应用的用户界面翻译成其他语言。 – stevex 2011-12-05 15:06:50

+0

这是整个话题中最糟糕的方式。您将无法本地化您的按钮,并会在'performSelector'上产生警告。 'performSelector'应该总是和静态选择器一起使用(非动态)。但是,你的问题是一个很好的问题。 – Martin 2015-09-02 14:02:34

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

我敢肯定你可以把更复杂的对象数组,包括按钮信息中然后将它全部包含在数组中。也许更好的错误对数组的索引检查....等

老实跟你,我从来没有想过这个模式多,直到我读的问题,所以这只是我的头顶部

+0

我敢肯定你不能代替零长期的使用数组过时的参数列表。但是那说,我不知道你可以参考那样的方法。这与我正在寻找的东西非常接近。 – 2010-10-20 04:31:36

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"]) 
    { 
     //... 
    } 
} 
+0

这是对索引进行匹配的明显改进。 – 2010-10-20 04:47:02

+6

不要忘记本地化 - 使用相同的字符串进行比较,这些字符串可能来自NSLocalizedString。 – stevex 2011-12-01 16:15:48

+0

@ stevex:你说得对。 – AechoLiu 2011-12-02 00:34:51

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

我完全同意这个问题。苹果在这里的设计鼓励使用魔术数字,我有点震惊地看到所有的解决方案都推荐使用硬编码的数字作为按钮索引。

这是我对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

很好 - 谢谢! – Gonen 2015-07-03 01:27:49