iOS开发做一个默默无私奉献的程序猿MVVM

iOS 使用MVVM模式实现Cell的点击响应

2017-07-25  本文已影响587人  劉光軍_MVP

卷首

最近新工作中用到的RAC+MVVM的开发模式,由于之前都是用MVC,从自己的菜鸡水平感觉这两种设计模式在思想上还是有些微区别的,然后自己也是看了挺多关于这两个模式异同与使用利弊的文章,但是说真的,代码这个东西光看看不出个花来,还是要写出来才能体会的更深,所以我不讲这两种模式的来龙去脉,我也讲不清 _, 要是看过比较多理论上的东西,再结合一下代码理理思路还是极好滴。

目的介绍

上面已经说了,这是一个关于怎样用代码实现mvvm的记录,本来之前就想写一个极其简单的tableview的代码就行了来着,但正好在项目里面遇到一个比较弱鸡的问题,当时思路一下卡住了,就是一个基础知识,但是当时没有想通,这种问题吧,在网上搜都不知道怎么搜,在开发的时候还是比较影响开发效率的,所以回来也把那一点儿东西加上了,其实并没有几行代码,就是想给自己提个醒。

效果图如下

segmentHeader.gif

代码介绍

效果图就是上面的,功能比较简单,大牛们肯定看都不带看的那种,哈哈哈,我就是比价喜欢从这种简单功能上弄懂一些东西,简单的都不会,就更不指望去搞高深的了。话不多说,首先看一下主要的代码结构:

屏幕快照 2017-07-25 下午11.11.00.png

这里分了四个文件夹用来存放view/model/controller/viewModel 当然有些有些view也可以放在viewController里面,这个并没有什么严格要求。viewModel主要是用来处理数据逻辑,将model进行处理之后和view/controller进行交互,我理解的它是一个数据加工工厂,这样做的目的也就是避免在controller里面处理大量与界面业务逻辑无关的工作嘛,将数据处理专门用viewModel进行处理。。。(具体的还是参考详细的文章吧,我大概一说)分别说一下各个模块中的代码实现吧,很好理解。

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.title = @"第一页";
    self.view.backgroundColor = [UIColor whiteColor];
    self.viewModel = [LGJMainViewModel new];
    [self configMainView];
}
#pragma mark - configView
- (void)configMainView {
    self.mainView = [[LGJMainView alloc] initWithViewModel:self.viewModel];
    self.mainView.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height);
    [self.view addSubview:self.mainView];
}
#pragma mark - tableView delegate&dataSource
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return [self.viewModel getSectionCount];
}

- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
    SectionModel *sectionModel = [self.viewModel getSectionModelWithSection:section];
    UILabel *headerLabel = [self configHeader];
    headerLabel.text = [NSString stringWithFormat:@"我是第%@个section", sectionModel.sectionName];
    return headerLabel;
}

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

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return [self.viewModel getCellCountWithIndexPath:section];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *idStr = @"LGJCell";
    LGJCell *cell = [tableView cellForRowAtIndexPath:indexPath];
    if (!cell) {
        cell = [[LGJCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:idStr];
    }
    cell.cellModel = [self.viewModel getRowModelWithIndexPath:indexPath];
    return cell;
}

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

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    [self.viewModel changeCellModelWithIndexPath:indexPath];
    [tableView reloadData];
}

#pragma mark - coustom tableViewHeader 
- (UILabel *)configHeader {
    UILabel *headerLabel = [UILabel new];
    headerLabel.backgroundColor = [UIColor whiteColor];
    headerLabel.font = [UIFont systemFontOfSize:14];
    headerLabel.textColor = [UIColor redColor];
    headerLabel.textAlignment = NSTextAlignmentCenter;
    return headerLabel;
}

.h
typedef void(^UpdateCellBlock)(NSIndexPath *indexPath);

@interface LGJMainViewModel : NSObject

@property (nonatomic, copy) UpdateCellBlock updateCellBlock;

- (void)changeCellModelWithIndexPath:(NSIndexPath *)indexPath;
- (RowModel *)getRowModelWithIndexPath:(NSIndexPath *)indexPath;
- (NSInteger)getCellCountWithIndexPath:(NSInteger)section;
- (NSInteger)getSectionCount;
- (SectionModel *)getSectionModelWithSection:(NSInteger)section;

在.m文件中我做了一个假数据,用来模拟section和cell中的数据,这个会有用的,就在下面我要说的那个坑。

.m
@interface LGJMainViewModel ()

@property (nonatomic, strong) NSMutableArray *listArr;//盛放所有model的数组

@end

@implementation LGJMainViewModel

- (instancetype)init {
    if (self = [super init]) {
        [self configModelArr];
    }
    return self;
}

- (void)configModelArr {
    
    self.listArr = [NSMutableArray array];
    
    for (int i = 0; i < 10; i++) {
        SectionModel *model = [SectionModel new];
        model.sectionName = [NSString stringWithFormat:@"%d", i];
        NSMutableArray *mutArr = [NSMutableArray array];
        for (int j = 0; j < 20; j++) {
            RowModel *rowModel = [RowModel new];
            rowModel.name = [NSString stringWithFormat:@"第%d行", j];
            rowModel.detail = [NSString stringWithFormat:@"我是第%d行, 多多指教", j];
            [mutArr addObject:rowModel];
        }
        model.rowModelArr = mutArr;
        [self.listArr addObject:model];
    }
}

这里就是我们在.h文件中看见的那些方法的实现了,在viewModel中对请求的数据或者本地的数据处理之后,返回给外部使用(这里说的不专业了,明白这个道理就好&—— &)

#pragma mark - get CellModel
- (RowModel *)getRowModelWithIndexPath:(NSIndexPath *)indexPath {
 
    SectionModel *secModel = [self.listArr objectAtIndex:indexPath.section];
    NSArray *rowArr = secModel.rowModelArr;
    RowModel *rModel = [rowArr objectAtIndex:indexPath.row];
    return rModel;
}

#pragma mark - cell/section Count
- (NSInteger)getSectionCount {
    return self.listArr.count;
}

- (NSInteger)getCellCountWithIndexPath:(NSInteger)section {
    SectionModel *secModel = [self.listArr objectAtIndex:section];
    return secModel.rowModelArr.count;
}

#pragma mark - get SectionModel
- (SectionModel *)getSectionModelWithSection:(NSInteger)section {
    SectionModel *sModel = [self.listArr objectAtIndex:section];
    return sModel;
}

这个坑就是在这儿,想实现的效果是,当我点击cell的时候,我替换这个cell对应的model数据,一开始是用的被注释掉的方法,这个稍微有些经验的都能想到这个不行,可是我就是那个掉坑的,本来想着我找到对应的sectionmodel,在sectionModel中找到对应的RowModel然后将新model替换掉旧的。perfect。。。运行之后发现是行不通的,然后用下面在数组中遍历查找的方法进行替换解决的。关于这问题我的理解是,数组中存放的是model的指针,我用newModel替换oldModel,替换的只是model的指针,但是数组中储存model的指针没有改变,所以数组并不会改变它保存的对应位置的指针,所以说数组中对应位置储存的还是oldModel的指针。 这里解释的不详细,下面有更详细的解释)

#pragma mark - change Cell Model
- (void)changeCellModelWithIndexPath:(NSIndexPath *)indexPath {
//    RowModel *rm = [RowModel new];
//    rm.name = @"新替换的name";
//    rm.detail = @"新替换的detail";
//    
//    SectionModel *sModel = [SectionModel new];
//    sModel = [self.listArr objectAtIndex:indexPath.section];
//    NSArray *tempCellArr = sModel.rowModelArr;
//    RowModel *rModel = [tempCellArr objectAtIndex:indexPath.row];
//    
//    rModel = rm;
//    
//    if (self.updateCellBlock) {
//        self.updateCellBlock(indexPath);
//    }

    for (int i = 0; i < self.listArr.count; i++) {
        SectionModel *sModel = [SectionModel new];
        if (i == indexPath.section) {
            sModel = [self.listArr objectAtIndex:i];
            for (int j = 0; j < sModel.rowModelArr.count; j++) {
                RowModel *rModel = [RowModel new];
                if (j == indexPath.row) {
                    rModel = [sModel.rowModelArr objectAtIndex:j];
                    rModel.name = @"替换***";
                    rModel.detail = @"我是被替换的新cell";
                }
            }
        }
    }
    
}
@interface SectionModel : NSObject

@property (nonatomic, copy) NSString *sectionName;
@property (nonatomic, strong) NSArray *rowModelArr;

@end

再次编辑 说明对上面说到的那个坑的一些解释和理解

经过评论里面小伙伴的提示,下班后回来再次对那块儿的解释详细说明一下

多谢评论里面小伙伴的提议,这种方法和for循环遍历赋值相比来说更好一点儿,因为如果数据量太大会造成性能影响,所以还是选择这种方法吧,多谢大家的交流,一起进步

总结

写这篇主要是记录一个最基本的mvvm思想的实现还有一个以后绝对不能再犯的错误,遇到问题多想想,别浮躁。晚安,明天还要上班,还有bug等我拯救。。。

demo:https://github.com/irembeu/LGJ_MVVM_TestDemo

上一篇 下一篇

猜你喜欢

热点阅读