如何处理动态创建的 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】:
如果您想在按钮和视图控制器类之间创建映射以在点击按钮时显示,您有两个简单的选择:
为每个按钮分配一个标签并创建一个将标签映射到类的字典(到NSString
或Class
):
@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
就可以了——如果你有很多并且需要区分它们,你可能需要设置宏以便你可以命名你的int
s。
UIButton *showModuleButton = [[UIButton alloc] init];
...
showModuleButton.tag = 1;
[showModuleButton addTarget:self action:@selector(showModule:) forControlEvents:UIControlEventTouchUpInside];
现在,每当您“触摸内部”按钮时,都会调用showModule:
,您可以在其中访问sender
的tag
属性以查看哪个按钮触发了事件。
-(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的主要内容,如果未能解决你的问题,请参考以下文章