如何处理动态创建的 UIButtons

Posted

技术标签:

【中文标题】如何处理动态创建的 UIButtons【英文标题】:How to handle dynamically created UIButtons 【发布时间】:2014-07-09 13:32:48 【问题描述】:

我有一个 UIViewController 作为一种菜单。菜单由多个框/图块组成,这些框/图块是包含带有描述的 UILabel 的 UIView、链接到显示更多信息的另一个 UIViewController 的 UIButton 和链接到显示应用程序另一个区域的 UIViewController 的另一个 UIButton。

目前有 3 个这样的盒子,但这会增加。目前我已经对每一个都进行了这样的硬编码:

- (void)drawProceduresBox

self.viewProceduresBox = [[UIView alloc] initWithFrame:CGRectMake((_screenWidth * 0.05), (_screenHeight * 0.35), (_screenWidth * 0.9), (_screenHeight * 0.25))];
[_viewProceduresBox setBackgroundColor:[UIColor whiteColor]];
_viewProceduresBox.layer.borderWidth = 1.0f;
_viewProceduresBox.layer.borderColor = [UIColor grayColor].CGColor;

CGFloat boxHeight = _viewProceduresBox.frame.size.height;
CGFloat boxWidth = _viewProceduresBox.frame.size.width;

self.buttonShowProcedures = [[UIButton alloc] init];
[_buttonShowProcedures setFrame:CGRectMake((boxWidth * 0.8), (boxHeight * 0.4), (boxWidth * 0.1), (boxHeight * 0.2))];
[_buttonShowProcedures setBackgroundColor:[UIColor greenColor]];
[_buttonShowProcedures addTarget:self action:@selector(showProcedures) forControlEvents:UIControlEventTouchUpInside];

_buttonshowMoreInfo = [[appButton alloc] init];
[_buttonshowMoreInfo setFrame:CGRectMake((boxWidth * 0.6), (boxHeight * 0.6), (boxWidth * 0.1), (boxHeight * 0.2))];
[_buttonshowMoreInfo setBackgroundColor:[UIColor blueColor]];
[_buttonshowMoreInfo setWhatToShow:@"vetMed"];
[_buttonshowMoreInfo addTarget:self action:@selector(showMoreInfo:) forControlEvents:UIControlEventTouchUpInside];

[_viewProceduresBox addSubview:_buttonShowProcedures];

可能有 10 多个这样的盒子,所以我想要一种动态创建它们的方法。这是我迄今为止尝试过的:

-(void)drawBox:(NSString*)moduleName boxFrame:(CGRect)boxFrame labelDesc:(NSString*)labelDesc


UIView *moduleInfoBox = [[UIView alloc] initWithFrame:boxFrame];
[moduleInfoBox setBackgroundColor:[UIColor purpleColor]];

CGFloat boxHeight = moduleInfoBox.frame.size.height;
CGFloat boxWidth = moduleInfoBox.frame.size.width;

UILabel *moduleLable = [[UILabel alloc] init];
[moduleLable setFrame:CGRectMake((boxWidth * 0.05), (boxHeight * 0.05), (boxWidth * 0.6), (boxHeight * 0.9))];
[moduleLable setBackgroundColor:[UIColor yellowColor]];
[moduleLable setText:labelDesc];

UIButton *showModuleButton = [[UIButton alloc] init];
[showModuleButton setFrame:CGRectMake((boxWidth * 0.8), (boxHeight * 0.15), (boxWidth * 0.1), (boxWidth * 0.2))];
[showModuleButton setBackgroundColor:[UIColor grayColor]];
[showModuleButton addTarget:self action:@selector(showModule:) forControlEvents:UIControlEventTouchUpInside];

UIButton *showMoreInfoButton = [[UIButton alloc] init];
[showMoreInfoButton setFrame:CGRectMake((boxWidth * 0.8), (boxHeight * 0.65), (boxWidth * 0.1), (boxWidth * 0.1))];
[showMoreInfoButton setBackgroundColor:[UIColor orangeColor]];
[showMoreInfoButton addTarget:self action:@selector(showMoreInfo:) forControlEvents:UIControlEventTouchUpInside];

[moduleInfoBox addSubview:moduleLable];
[moduleInfoBox addSubview:showModuleButton];
[moduleInfoBox addSubview:showMoreInfoButton];

_scrollHeight = _scrollHeight + (moduleInfoBox.frame.origin.y + moduleInfoBox.frame.size.height);

[_scrollView addSubview:moduleInfoBox];



我会这样调用方法(在这种情况下是三个盒子):

CGRect formularyFrame = CGRectMake((_screenWidth * 0.05), (_screenHeight * 0.05), (_screenWidth * 0.9), (_screenHeight * 0.25));
CGRect proceduresFrame = CGRectMake((_screenWidth * 0.05), (_screenHeight * 0.35), (_screenWidth * 0.9), (_screenHeight * 0.25));
CGRect vetMedFrame = CGRectMake((_screenWidth * 0.05), (_screenHeight * 0.65), (_screenWidth * 0.9), (_screenHeight * 0.25));
[self drawBox:@"Formulary" boxFrame:formularyFrame labelDesc:@"Desc about formulary module"];
[self drawBox:@"Procedures" boxFrame:proceduresFrame labelDesc:@"Desc about procedures module"];
[self drawBox:@"VetMed" boxFrame:vetMedFrame labelDesc:@"Desc about vetMed module"];

我遇到的问题是动态生成的 UIButtons 需要链接到相关的东西,在这种情况下是 UIViewControllers。每个按钮都有一个选择器,可以触发“showModule”和“showModuleInfo”方法。

在这些方法中,我如何知道按下了哪个 UIButton,从而知道要将 navigationController 推送到哪个 UIViewController?基本上我想要一些类似的东西:

-(void)showModule:(NSString*)moduleToShow

    [self.navigationController pushViewController:moduleToShow animated:NO];

但据我了解,您不能将参数添加到“@selector”赋值:

[_buttonshowMoreInfo addTarget:self action:@selector(showMoreInfo:) forControlEvents:UIControlEventTouchUpInside];

我应该怎么做?

【问题讨论】:

好吧,动态创建它们。如果你愿意,可以通过一个循环。当您不知道可能有多少时,将它们放在一个数组中。或者使用表格并将按钮添加到单元格。或者根本不将它们保存在任何局部变量中,而是为它们分配一个标签。单击按钮后,您可以使用标签来识别单击了哪个按钮 - 如果您需要知道的话。 这就是我最终想要实现的目标。我将有一个循环遍历存储在 SQLite DB 中的模块名称数组,然后使用相关链接构建每个框,这些链接指向动态创建的 UIViewController。我只是不知道如何将动态创建的 UIButtons 与加载动态创建的 UIVCs 的方法联系起来。 不知何故,您将不得不知道要为每个按钮调用哪个视图控制器。如果您可以使用整数作为 id 来做到这一点,那么假设是数组的索引。然后使用 for 循环创建以 i 作为循环变量的视图。将i 分配给每个按钮的tag 属性。当按下按钮时,将调用一个动作,sender 被移交给该动作。发送者是一个UIView 子类,它就是被按下的按钮。获取它的tag 值并从那里开始...... 【参考方案1】:

如果您想在按钮和视图控制器类之间创建映射以在点击按钮时显示,您有两个简单的选择:

    为每个按钮分配一个标签并创建一个将标签映射到类的字典(到NSStringClass):

    @property(nonatomic, strong) NSMutableDictionary *tagClassDic;
    
    -(void)drawBox:(NSString*)moduleName boxFrame:(CGRect)boxFrame labelDesc:(NSString*)labelDesc buttonTag:(NSInteger)tag 
        ...
        showModuleButton.tag = tag;
    
    
    -(void)showModule:(UIButton *)sender 
        NSString *classStr = self.tagClassDic[@(sender.tag)];
        [self.navigationController pushViewController:[NSClassFromString(classStr) new] animated:NO];
        // or, if you decide to store classes in the dictionary:
        [self.navigationController pushViewController:[self.tagClassDic[@(sender.tag)] new] animated:NO];
    
    
    ...
    
    self.tagClassDic = [NSMutableDictionary dictionary];
    
    NSInteger firstTag = 1;
    [self drawBox:@"Formulary" boxFrame:formularyFrame labelDesc:@"Desc about formulary module" buttonTag:firstTag];
    self.tagClassDic[@(firstTag)] = @"ViewController"; // or [ViewController class]
    

    将关联对象(同样是字符串或类)直接设置为按钮对象:

    static const char *kViewControllerClassKey = "viewControllerClassKey";
    
    -(void)drawBox:(NSString*)moduleName boxFrame:(CGRect)boxFrame labelDesc:(NSString*)labelDesc viewControllerClassStr:(NSString*)classStr 
        ...
        objc_setAssociatedObject(showModuleButton, kViewControllerClassKey, classStr, OBJC_ASSOCIATION_COPY_NONATOMIC);
    
    
    -(void)showModule:(UIButton *)sender 
        NSString *classStr = objc_getAssociatedObject(sender, kViewControllerClassKey);
        [self.navigationController pushViewController:[NSClassFromString(classStr) new] animated:NO];
    
    
    ...
    
    [self drawBox:@"Formulary" boxFrame:formularyFrame labelDesc:@"Desc about formulary module" viewControllerClassStr:@"ViewController"];
    

【讨论】:

那里的第二个选项似乎很有意义,可能正是我所需要的。我宁愿不使用标签作为生成 UIButtons 的方法,每次“运行”会产生 2 个不同的按钮。还有标签我需要某种查找?标签 1 = catsVC,标签 2 = dogsVC.. 等等 在第一个选项中通过字典进行查找:键是按钮的标签,值是类。 不知道为什么当时没有接受这个答案,但这实际上是为我解决了这个问题。【参考方案2】:

点击UIButton 时调用的方法有点偏离。例如,现在你有:

// if a button action triggers this as your code is written, it won't have a valid string.
-(void)showModule:(NSString*)moduleToShow

    [self.navigationController pushViewController:moduleToShow animated:NO];

这意味着当您点击按钮时showModule: 方法将接收NSString 对象,但实际上它会接收触发事件的UIButton 对象。像这样的:

-(void)showModule:(UIButton *)sender

    // use information about the button that sent the action

了解点击了哪个按钮的常用方法是在创建按钮时为其设置唯一标签。如果你只有几个,在它们上面编码一个int 就可以了——如果你有很多并且需要区分它们,你可能需要设置宏以便你可以命名你的ints。

UIButton *showModuleButton = [[UIButton alloc] init];
...
showModuleButton.tag = 1;
[showModuleButton addTarget:self action:@selector(showModule:) forControlEvents:UIControlEventTouchUpInside];

现在,每当您“触摸内部”按钮时,都会调用showModule:,您可以在其中访问sendertag 属性以查看哪个按钮触发了事件。

-(void)showModule:(UIButton *)sender

    // use information about the button that sent the action
    if(sender.tag == 1)
    
        // you know which button triggered the event now!
    

【讨论】:

感谢您,我在最后发布的示例更像是“我想要这样的”,而不是我使用过的。我尝试使用标签,但我将如何分配标签?目前,相同的代码用于创建每个 UIButton。此外,创建按钮的方法会创建两个按钮,而不是一个。 @Ryan 所以你是说你有 2 个以相同方式创建的按钮,但你希望它们有不同的操作? 添加的每个 UIView 都有 1 个 UILabel 和 2 个 UIButtons。 这不是最干净的选项,但您可以将 BOOL 或类似的东西传递给您的视图创建方法(一个视图为 false,另一个视图为 true)并根据该值更改标签当你创建它们时(伪代码:if(true) tag=1; else tag=47; 这是我想要的“解决方法”,但想知道动态创建未知数量的 UIButtons 的“正确方法”是什么,每个 UIButtons 都略有不同。

以上是关于如何处理动态创建的 UIButtons的主要内容,如果未能解决你的问题,请参考以下文章

C# 如何处理以编程方式创建的 ColorAnimation 和 Storyboard?

如何处理完全动态的 JSON 响应

如何为具有动态宽度的 UIButtons 设置背景?

如何处理/限制用户对 servlet 和 jsp 的访问?

如何处理 .net MVC Core 中的动态错误页面?

mySQL 如何处理 ORDER BY 中的动态值