公司有这么个需求, 要求用户在线填写单选形式的答题,效果如下:
这里要注意的是, 每题对应的ABCD选项都有对应的分数, 提交后计算总分数, 每题单选,共10道题, 并且在页面滑动回之前答过的题后, 要显示之前已经选择过的选项
当然其实这个简单的也面, 要实现并不难, UIScollView + 自定义UIView 容器就可以实现, 公司安卓端就是这么实现的, 但是缺点也很显著, 同一个页面控件太多, 最终导致了项目崩溃
我个人在写项目的时候, 能用UITableView 的时候尽量不会使用UIScrollView, 所以我第一选择是使用UITableViewController, 但是使用不分组形式, 因为一开始看起来每道题目都是类似的界面, 做起来应该会很简单, 原数据保存在risk.json中
以下是我一开始的部分代码
- (void)loadRiskData
{
#pragma mark - Table view data source
if (!_dataArray) {
_dataArray = [NSArray new];
// 解析本地JSON文件获取数据,生产环境中从网络获取JSON
NSString *path = [[NSBundle mainBundle] pathForResource:@"risk" ofType:@"json"];
NSError *error = nil;
NSData *data = [[NSData alloc] initWithContentsOfFile:path];
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
_dataArray = [JSRiskEvaluateModel objectArrayWithKeyValuesArray:dict[@"question"]];
cellMarkArray = [NSMutableArray array];
for (int i = 0; i < _dataArray.count; i++)
{
cellMarkDic = [NSMutableDictionary dictionary];
[cellMarkDic setObject:@"0" forKey:@"cellMark"];
[cellMarkArray addObject:cellMarkDic];
}
NSLog(@"_dataArray.firstObject = %@ ", _dataArray);
if (error) {
NSLog(@"address.json - fail: %@", error.description);
}
}
}
在这里将risk.json中的数据解析出来, 后来做着做着发现一个问题, 就是我使用的是不分组形式, 一道题里的选项,全部放在一个cell中
当你点击一个cell时, 在- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
方法中,没有办法区分你到底是选择了哪道题, 那么你选择的那个题目的分数也就无法取出来
所以我又放弃了这种做法, 改为使用分组形式, 一个问题为一个组, 一个选择项为一个UITableViewCell
可是做着又发现一个问题, 循环利用, cell 界面需要将你点击的那道题目的按钮变为选中状态, 这个状态是要保存的, 当你重新滚回你选择的题目时,要有之前选中的题目按钮为选中状态, 界面不再试一个静态的数据页面, 你在重新滚回这个界面时, 从新添加数据已经不能满足需求了
为了解决这个问题
-
首先, 我在生成cell时, 不使用循环生成,
这样的话,生成的cell数据正确, 但是按钮点击的状态没法实现, 所以我使用了字典, 每组一个字典,分别用indexpath.section 作为key, 被选择的index.row作为value, 然后将字典按顺序保存在数组中
根据苹果的特性, 当上拉或者下拉UITableViewController, 一个UITableViewCell 即将出现在屏幕上时, 会调用- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
方法, 所以我们在这里来判断他的点击状态
这里取出数组中对应的位置,进行重新生成被点中状态的cell状态
if (indexPath.section == 0)
{
// 如果是字典类, 说明这个组的在数组中的位置为字典, 说明之前有点击过这个组的按钮
if ([self.cellMarkArray[indexPath.section] isKindOfClass:[NSMutableDictionary class]] )
{
UITableViewCell * cell;
NSMutableDictionary *dic = self.cellMarkArray[indexPath.section];
if ([[dic objectForKey:@"0"] isEqualToString:@"1"])
{
cell = [self SelectCellOneView:indexPath andTableView:tableView andRiskModel:riskModel];
}else if ([[dic objectForKey:@"0"] isEqualToString:@"2"])
{
cell = [self SelectCellTwoView:indexPath andTableView:tableView andRiskModel:riskModel];
}else if ([[dic objectForKey:@"0"] isEqualToString:@"3"])
{
cell = [self SelectCellThreeView:indexPath andTableView:tableView andRiskModel:riskModel];
}else if ([[dic objectForKey:@"0"] isEqualToString:@"4"])
{
cell = [self SelectCellForeView:indexPath andTableView:tableView andRiskModel:riskModel];
}else
{
// 普通没有被选中状态
cell = [self nomallCellView:indexPath andTableView:tableView andRiskModel:riskModel];
}
return cell;
}else
{
// 没有点击过, 就返回没选中的cell
// 普通没有被选中状态
UITableViewCell * cell = [self nomallCellView:indexPath andTableView:tableView andRiskModel:riskModel];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
return cell;
}
以下是示例第一个选项被点中时, 生成的cell状态
- (UITableViewCell *)SelectCellOneView:(NSIndexPath *)indexPath andTableView:(UITableView *)tableView andRiskModel:(JSRiskEvaluateModel *)riskModel
{
if (indexPath.row == 0)
{
JSGotoRiskQuestionTableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:questionTitle];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.questionTitleStr = riskModel.questionTitle;
return cell;
}else if (indexPath.row == 1)
{
// JSGotoRiskCell *cell = [tableView dequeueReusableCellWithIdentifier:question];
JSGotoRiskCell *cell = [tableView cellForRowAtIndexPath:indexPath];
if (cell == nil) {
cell = [[[NSBundle mainBundle] loadNibNamed:NSStringFromClass([JSGotoRiskCell class]) owner:nil options:nil] lastObject];
}
cell.line.hidden = YES;
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.questionLabel.text = riskModel.questionA;
[cell.questionBtn setImage:[UIImage imageNamed:@"icon_a_on"] forState:UIControlStateNormal];
[cell.questionBtn setImage:[UIImage imageNamed:@"icon_a"] forState:UIControlStateSelected];
[cell.questionBtn setSelected:YES];
return cell;
}else if (indexPath.row == 2)
{
// JSGotoRiskCell *cell = [tableView dequeueReusableCellWithIdentifier:question];
JSGotoRiskCell *cell = [tableView cellForRowAtIndexPath:indexPath];
if (cell == nil) {
cell = [[[NSBundle mainBundle] loadNibNamed:NSStringFromClass([JSGotoRiskCell class]) owner:nil options:nil] lastObject];
}
cell.line.hidden = YES;
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.questionLabel.text = riskModel.questionB;
[cell.questionBtn setImage:[UIImage imageNamed:@"icon_b_on"] forState:UIControlStateNormal];
[cell.questionBtn setImage:[UIImage imageNamed:@"icon_b"] forState:UIControlStateSelected];
return cell;
}else if (indexPath.row == 3)
{
// JSGotoRiskCell *cell = [tableView dequeueReusableCellWithIdentifier:question];
JSGotoRiskCell *cell = [tableView cellForRowAtIndexPath:indexPath];
if (cell == nil) {
cell = [[[NSBundle mainBundle] loadNibNamed:NSStringFromClass([JSGotoRiskCell class]) owner:nil options:nil] lastObject];
}
cell.line.hidden = YES;
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.questionLabel.text = riskModel.questionC;
[cell.questionBtn setImage:[UIImage imageNamed:@"icon_c_on"] forState:UIControlStateNormal];
[cell.questionBtn setImage:[UIImage imageNamed:@"icon_c"] forState:UIControlStateSelected];
return cell;
}else if (indexPath.row == 4)
{
// JSGotoRiskCell *cell = [tableView dequeueReusableCellWithIdentifier:question];
JSGotoRiskCell *cell = [tableView cellForRowAtIndexPath:indexPath];
if (cell == nil) {
cell = [[[NSBundle mainBundle] loadNibNamed:NSStringFromClass([JSGotoRiskCell class]) owner:nil options:nil] lastObject];
}
if (indexPath.section == 8)
{
cell.line.hidden = YES;
}else
{
cell.line.hidden = NO;
}
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.questionLabel.text = riskModel.questionD;
[cell.questionBtn setImage:[UIImage imageNamed:@"icon_d_on"] forState:UIControlStateNormal];
[cell.questionBtn setImage:[UIImage imageNamed:@"icon_d"] forState:UIControlStateSelected];
return cell;
}else
{
// JSGotoRiskCell *cell = [tableView dequeueReusableCellWithIdentifier:question];
JSGotoRiskCell *cell = [tableView cellForRowAtIndexPath:indexPath];
if (cell == nil) {
cell = [[[NSBundle mainBundle] loadNibNamed:NSStringFromClass([JSGotoRiskCell class]) owner:nil options:nil] lastObject];
}
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.questionLabel.text = riskModel.questionE;
[cell.questionBtn setImage:[UIImage imageNamed:@"icon_e_on"] forState:UIControlStateNormal];
[cell.questionBtn setImage:[UIImage imageNamed:@"icon_e"] forState:UIControlStateSelected];
return cell;
}
}
有个注意点就是数组的插入不要使用insertObject, 我最后使用了repalceObjectAtIndex
的方式将字典存放在对应的数组位置中, 因为我发现使用insert的方式, 偶尔在来回滚动的过程中, 生成数组时有时会取不出对应的字典, 导致后面的cell重现变为没有选中状态
单选实现的文件已经放在了gitHub 上, 小家可以来这里下载问卷调查(单选)
文件暂时还没有整理, 有点乱,后期有时间我会再整理, 但是不影响使用, 打开可直接运行, 希望对小伙伴们有用