iOS MVC实例讲解

MVC是一种设计模式 面试的时候也经常被问到 一般都会说Model-View-Controller三者的关系 今天没事 就写篇文章 用实际的开发运用MVC
先看效果图

image.png

这是个答题的页面 首先进来 请求接口 返回 顶部的轮播图图片数组 中间的标题 中间的内容 答题奖励价格 投放和剩余数量等等信息 还有下面的题目信息 然后底部有一个提交按钮 提交答案

很典型的 接口--- 展示数据----提交数据的 常见开发场景

首先页面分解 下面的答题的题目和答案 可以采用tableview去布局 那么顶部的(轮播图及具体信息)就是一个view 当做tableview的headerview 底部的提交按钮就是tableview的footerview

所以第一步 先把顶部的部分封成一个View

image.png

里面的控件就这么多 都声明下 然后 设置控件的属性

image.png

值得注意的是内容的label需要把numberOfLines设置为0

然后就是基本的masonry布局

image.png

根据接口数据创建model 然后在控制器里去请求数据 然后转成model 设置给这个view
在model的set方法里 给view的控件赋值
[图片上传中...(image.png-7cee49-1532678118564-0)]

/**
 根据接口返回数据去设置头视图

 @param model 大的模型
 */
- (void)setUpHeaderViewWith:(SHAdverDetailModel *)model
{
    NSString *introduce = model.introduce;
    CGRect rect = [introduce boundingRectWithSize:CGSizeMake(SHScreenW - 26, CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName : [UIFont systemFontOfSize:13.f ]} context:nil];
    SHAnswerHeaderView *headerView = [[SHAnswerHeaderView alloc]initWithFrame:CGRectMake(0, 0, SHScreenW, 315 + rect.size.height)];
    headerView.model = model;
    self.tableView.tableHeaderView = headerView;
}
image.png

其中boundingRectWithSize是手动计算内容高度的方法

这样就把一个带有接口数据的动态高度的view设置成了tableview的头视图
控制器里的代码此时有 发起网络请求 ---- 拿到数据 ---- 创建头视图

然后分析需要解决的点
1:轮播图下方内容高度不固定 高度随内容的增长而增长 (上面已解决)
2:题目的选择 单选 及 选中效果
3:如何优雅的传入数据拿到每道题选择的答案

下面的题目布局 分析下 每个题目作为一个section 题目是section的headerView(自定义的view 需要传入 下标 和 题目字符串) 每条答案是cell (其中前面的选中效果需要控制蓝色view的显示与隐藏 ) 至于选择一个答案之后需要取消上一个选中及设置当前选中 我们可以在题目的model里面自定义一个字段

image.png

上面的5个字段是接口返回的题目和答案 还有正确选项
我添加一个字段

//选择的下标  默认为-1
@property (nonatomic, assign) NSInteger seleteIndex;

然后在model的init方法里给默认值-1

- (instancetype)init
{
    if (self = [super init]) {
        self.seleteIndex = -1;
    }
    return self;
}

这个值的意思是我选中的是第几个答案

然后在cell的.h里暴露一个布尔值和字符串

#pragma mark --- setting
- (void)setIsSelete:(BOOL)isSelete
{
    _isSelete = isSelete;
    if (isSelete) {
        self.smallCycleView.hidden = NO;
    }else{
        self.smallCycleView.hidden = YES;
    }
}

- (void)setQustionStr:(NSString *)qustionStr
{
    _qustionStr = qustionStr;
    self.answerL.text = qustionStr;
}

在set方法里去控制蓝色区域的显示与隐藏

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    [tableView deselectRowAtIndexPath:indexPath animated:YES];
    SHAnswerItemModel *model = self.problemsList[indexPath.section];
    model.seleteIndex = indexPath.row;
    self.problemsList[indexPath.section] = model;
    NSIndexSet *indexSet=[[NSIndexSet alloc]initWithIndex:indexPath.section];
    [self.tableView reloadSections:indexSet withRowAnimation:UITableViewRowAnimationAutomatic];
}

然后在cell的点击方法里 找到题目的model 把自定义的数据换成cell的下标再赋给model
再调tableview刷新单个section的方法

在cellforrow方法里


image.png

如果cell的下标 等于对应model的seleteIndex 就代表是选中的那个cell 所以这个cell要被选择 其他的不被选择

第二个问题也解决了

第三个问题 如何优雅的传入数据拿到每道题选择的答案

因为接口返回了正确答案 所以 我本地就可以判断回答对了几个 哪几个回答对了
在题目的model里面依然自定义一个字段

//是否选择正确  重写get方法
@property (nonatomic, assign) BOOL isCorrect;

重写get方法

- (BOOL)isCorrect
{
    switch (self.seleteIndex) {
        case 0:
            if ([self.correctResponse isEqual:@"A"]) {
                return YES;
            }
            break;
        case 1:
            if ([self.correctResponse isEqual:@"B"]) {
                return YES;
            }
            break;
        case 2:
            if ([self.correctResponse isEqual:@"C"]) {
                return YES;
            }
            break;
        default:
            break;
    }
    return NO;
}

下标0对应着返回的”A“

最后收尾的就是把提交的view也封成一个view 把按钮点击的事件通过block传出来

image.png

最后在提交的方法里

 for (int i = 0; i < self.problemsList.count; i ++) {
        SHAnswerItemModel *model = self.problemsList[i];
        if (model.seleteIndex == -1) {
            NSLog(@"题目必须全部作答");
            return;
        }
        NSString *str = model.isCorrect ? @"答对了" : @"答错了";
        NSLog(@"第%ld题%@,正确答案%@,你选择了第%ld个",i,str,model.correctResponse,model.seleteIndex);
    }

第几题 是否正确 选择的答案 正确答案都一目了然

最后看下控制器的代码

@interface SHAnswerQuesVController ()

@property (nonatomic, strong) UITableView *tableView;

@property (nonatomic, strong) NSMutableArray *problemsList;

@end

@implementation SHAnswerQuesVController

#pragma mark --- lazying
- (NSMutableArray *)problemsList
{
    if (!_problemsList) {
        _problemsList = [[NSMutableArray alloc]init];
    }
    return _problemsList;
}

- (UITableView *)tableView
{
    if (!_tableView) {
        _tableView = [[UITableView alloc]initWithFrame:CGRectZero style:UITableViewStyleGrouped];
        _tableView.backgroundColor = [UIColor whiteColor];
        _tableView.delegate = self;
        _tableView.dataSource = self;
        _tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
        [self.view addSubview:_tableView];
    }
    return _tableView;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    [self layout];
    self.navigationItem.title = @"点点答题";
    [self loadAdvertiseDetailData];
}

- (void)layout
{
    [self.tableView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.right.bottom.top.mas_equalTo(self.view);
    }];
    SHAnswerFooterView *footerView = [[SHAnswerFooterView alloc]initWithFrame:CGRectMake(0, 0, SHScreenW, 100)];
    __weak __typeof(&*self) weakSelf = self;
    footerView.commitBlock = ^{
      //点击提交
        [weakSelf commit];
    };
    self.tableView.tableFooterView = footerView;
}

- (void)loadAdvertiseDetailData
{
    SHLog(@"%d", _listModel.ID)
    SHWeakSelf
    NSDictionary *dic = @{
                          @"adId":@(_listModel.ID)
                          };
    [MBProgressHUD showHUDAddedTo:self.view animated:YES];
    [[SG_HttpsTool sharedSG_HttpsTool] postWithURL:SHAdverDetailUrl params:dic success:^(id JSON, int code, NSString *msg) {
        SHLog(@"%d", code)
        SHLog(@"%@", JSON)
        [MBProgressHUD hideHUDForView:weakSelf.view];
        if (code == 0) {
            NSDictionary *responDict = (NSDictionary *)JSON;
            NSDictionary *ad = responDict[@"ad"];
            SHAdverDetailModel *model = [SHAdverDetailModel mj_objectWithKeyValues:ad];
#warning test  测试长度很多的时候高度的变化
//            self.model.introduce = @“一个很长的字符串”
            self.problemsList = [SHAnswerItemModel mj_objectArrayWithKeyValuesArray:model.problems];
            [self setUpHeaderViewWith:model];
            [self.tableView reloadData];
        }
    } failure:^(NSError *error) {
        
    }];
}

/**
 根据接口返回数据去设置头视图

 @param model 大的模型
 */
- (void)setUpHeaderViewWith:(SHAdverDetailModel *)model
{
    NSString *introduce = model.introduce;
    CGRect rect = [introduce boundingRectWithSize:CGSizeMake(SHScreenW - 26, CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName : [UIFont systemFontOfSize:13.f ]} context:nil];
    SHAnswerHeaderView *headerView = [[SHAnswerHeaderView alloc]initWithFrame:CGRectMake(0, 0, SHScreenW, 315 + rect.size.height)];
    headerView.model = model;
    self.tableView.tableHeaderView = headerView;
}

//提交
- (void)commit
{
    for (int i = 0; i < self.problemsList.count; i ++) {
        SHAnswerItemModel *model = self.problemsList[i];
        if (model.seleteIndex == -1) {
            NSLog(@"题目必须全部作答");
            return;
        }
        NSString *str = model.isCorrect ? @"答对了" : @"答错了";
        NSLog(@"第%ld题%@,正确答案%@,你选择了第%ld个",i,str,model.correctResponse,model.seleteIndex);
    }
}

#pragma mark --- UITableViewDelegate, UITableViewDataSource

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return self.problemsList.count;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return 3;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    SHSeleteAnswerCell *cell = [tableView dequeueReusableCellWithIdentifier:NSStringFromClass([SHSeleteAnswerCell class])];
    if (!cell) {
        cell = [[SHSeleteAnswerCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:NSStringFromClass([SHSeleteAnswerCell class])];
    }
    SHAnswerItemModel *model = self.problemsList[indexPath.section];
    NSString *qustionStr;
    switch (indexPath.row) {
        case 0:
            qustionStr = [NSString stringWithFormat:@"A:%@",model.answerA];
            break;
        case 1:
            qustionStr = [NSString stringWithFormat:@"B:%@",model.answerB];
            break;
        case 2:
            qustionStr = [NSString stringWithFormat:@"C:%@",model.answerC];
            break;
        default:
            break;
    }
    cell.qustionStr = qustionStr;
    
    if (indexPath.row == model.seleteIndex) {
        cell.isSelete = YES;
    }else{
        cell.isSelete = NO;
    }
    
    return cell;
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    return 30;
}

- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
    SHAnswerItemModel *model = self.problemsList[section];
    SHAnswerSectionHeaderView *sectionHeaderView = [[SHAnswerSectionHeaderView alloc]initWithFrame:CGRectMake(0, 0, SHScreenW, 30) section:section quesionStr:model.question];
    sectionHeaderView.backgroundColor = [UIColor whiteColor];
    return sectionHeaderView;
}

- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
    return 30;
}

- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section
{
    return [UIView new];
}

- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section
{
    return 0.01;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    [tableView deselectRowAtIndexPath:indexPath animated:YES];
    SHAnswerItemModel *model = self.problemsList[indexPath.section];
    model.seleteIndex = indexPath.row;
    self.problemsList[indexPath.section] = model;
    NSIndexSet *indexSet=[[NSIndexSet alloc]initWithIndex:indexPath.section];
    [self.tableView reloadSections:indexSet withRowAnimation:UITableViewRowAnimationAutomatic];
}


一共200行不到

你可能感兴趣的:(iOS MVC实例讲解)