代码之家  ›  专栏  ›  技术社区  ›  Rob Van Dam

将uiactionsheet选项连接到操作的正确方法

  •  3
  • Rob Van Dam  · 技术社区  · 14 年前

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

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

    我不想开始一场编程语言的圣战,我只是想知道在这个场景中(我相信目标C中的许多其他人),将按钮字符串列表连接到操作列表的正确设计模式是什么。

    5 回复  |  直到 9 年前
        1
  •  4
  •   AechoLiu    14 年前

    我喜欢这边

    - (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"])
        {
            //...
        }
    }
    
        2
  •  3
  •   stone    9 年前

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

    这是我对斯威夫特的解决方案。

    • 为每个按钮标题创建一个包含项的枚举,例如:
    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
        }
    }
    
        3
  •  1
  •   Aaron Saunders    14 年前

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

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

    然后在迪迪米西斯威特百宁地塞米塞米塞米塞米塞米塞米塞米塞米塞米塞米塞米塞米塞米塞米塞米塞米塞米塞米塞米塞米塞米塞米塞米塞米塞米

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

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

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

        4
  •  1
  •   boecko    12 年前

    那个怎么样?

    通过这种方式,您不必担心索引,因为按钮和操作是在同一位置添加的。

    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;
    }
    
        5
  •  -1
  •   Rob Van Dam    14 年前

    基于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")
        }
    }