iOS 使用MVVM模式實現(xiàn)Cell的點擊響應(yīng)

2017-08-06iOS開發(fā)

卷首

最近新工作中用到的RAC+MVVM的開發(fā)模式,由于之前都是用MVC官觅,從自己的菜雞水平感覺這兩種設(shè)計模式在思想上還是有些微區(qū)別的,然后自己也是看了挺多關(guān)于這兩個模式異同與使用利弊的文章阐污,但是說真的休涤,代碼這個東西光看看不出個花來,還是要寫出來才能體會的更深,所以我不講這兩種模式的來龍去脈功氨,我也講不清 ^_^序苏, 要是看過比較多理論上的東西,再結(jié)合一下代碼理理思路還是極好滴捷凄。

目的介紹

上面已經(jīng)說了忱详,這是一個關(guān)于怎樣用代碼實現(xiàn)mvvm的記錄,本來之前就想寫一個極其簡單的tableview的代碼就行了來著跺涤,但正好在項目里面遇到一個比較弱雞的問題匈睁,當時思路一下卡住了,就是一個基礎(chǔ)知識桶错,但是當時沒有想通航唆,這種問題吧,在網(wǎng)上搜都不知道怎么搜院刁,在開發(fā)的時候還是比較影響開發(fā)效率的糯钙,所以回來也把那一點兒東西加上了,其實并沒有幾行代碼退腥,就是想給自己提個醒任岸。

效果圖如下


代碼介紹

效果圖就是上面的,功能比較簡單阅虫,大牛們肯定看都不帶看的那種演闭,哈哈哈,我就是比價喜歡從這種簡單功能上弄懂一些東西颓帝,簡單的都不會米碰,就更不指望去搞高深的了。話不多說购城,首先看一下主要的代碼結(jié)構(gòu):

這里分了四個文件夾用來存放view/model/controller/viewModel 當然有些有些view也可以放在viewController里面吕座,這個并沒有什么嚴格要求。viewModel主要是用來處理數(shù)據(jù)邏輯瘪板,將model進行處理之后和view/controller進行交互吴趴,我理解的它是一個數(shù)據(jù)加工工廠,這樣做的目的也就是避免在controller里面處理大量與界面業(yè)務(wù)邏輯無關(guān)的工作嘛侮攀,將數(shù)據(jù)處理專門用viewModel進行處理锣枝。。兰英。(具體的還是參考詳細的文章吧撇叁,我大概一說)分別說一下各個模塊中的代碼實現(xiàn)吧,很好理解畦贸。

Controller

控制器中就這幾行代碼陨闹,將自定義的mainView進行frame布局楞捂,初始化viewModel,是不是看著要比以前養(yǎng)眼了趋厉。

- (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];}

LGJMainView

就是我們看到的tableview一個自定義的view寨闹,用來“盛放”我們自定義的view,這里貼上來的代碼就是將tableview單獨放在這個view里面進行出來君账,當然這里的這些代理方法如果你想使這個view簡化還是可以將他們封裝出來的繁堡,我之前有寫過一個對tableview代理方法優(yōu)化的記錄iOS實現(xiàn)UITableViewDataSource與Controller的分離可以參考這個進一步優(yōu)化,這里面沒有什么多說的乡数,就有一點和之前不一樣的帖蔓,就是多了幾個self.viewModel的方法,這個我們下面說

#pragma mark - tableView delegate&dataSource- (NSInteger)

numberOfSectionsInTableView:(UITableView *)tableView {

return [self.viewModel getSectionCount];}- (UIView *)tableView:(UITableView *)tableView viewForHeader

InSection:(NSInteger)section { ? ?SectionModel *sectionModel = [self.viewModel getSectionModel

WithSection:section]; ? ?UILabel *headerLabel =

[self configHeader]; ? ?headerLabel.text = [NSString stringWithFormat:

@"我是第%@個section", sectionModel.sectionName];

return headerLabel;}- (CGFloat)tableView:(UITableView *)tableView heightForHeader

InSection:(NSInteger)section {

return 50;}- (NSInteger)tableView:(UITableView *)tableView numberOfRows

InSection:(NSInteger)section {

return [self.viewModel getCellCountWithIndexPath:section];}- (UITableViewCell *)tableView:(UITableView *)tableView cellFor

RowAtIndexPath:(NSIndexPath *)indexPath {

static NSString *idStr = @"LGJCell"; ? ?LGJCell *cell = [tableView cellForRowAtIndexPath:indexPath];

if (!cell) { ? ? ? ?cell = [[LGJCell alloc] initWithStyle:UITableView

CellStyleDefault reuseIdentifier:idStr]; ? ?} ? ?cell.cellModel = [self.viewModel getRowModelWithIndexPath:

indexPath];

return cell;}- (CGFloat)tableView:(UITableView *)tableView heightForRowAt

IndexPath:(NSIndexPath *)indexPath {

return 50;}- (void)tableView:(UITableView *)tableView didSelectRowAt

IndexPath:(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;}

LGJViewModel

看一下.h文件中瞳脓,主要就是一些外部需要調(diào)用的方法塑娇,比如在這里我們使用比較多的就是和tableview代理方法相關(guān)的很多方法比較多,如果是在MVC中劫侧,那么我們這些數(shù)據(jù)操作很有可能會寫在controller里面埋酬,controller里面的內(nèi)容也就不像我們剛看見的那樣簡潔了,還有一個就是我在.h文件中聲明了一個block烧栋,其實用RAC+MVVM開發(fā)的話写妥,RAC框架有很多自身封裝好的的block也就是Signal供我們使用,也就減少了我們比較容易頭疼也比較容易忽略的block的循環(huán)引用和內(nèi)存泄漏审姓,等我再熟悉熟悉RAC再專門去說它吧珍特,這里我們就用block先這樣處理,這個不是不可能的魔吐。(在這里這個block沒有用到扎筒,一開始想用來處理一些東西來著,后來沒有用酬姆,之所以沒有刪嗜桌,是想說一下,在mvvm中如果我們沒有用RAC框架辞色,我們可以用block來進行一些回調(diào)操作)

.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文件中我做了一個假數(shù)據(jù)骨宠,用來模擬section和cell中的數(shù)據(jù),這個會有用的相满,就在下面我要說的那個坑层亿。

.m

@interface LGJMainViewModel ()

@property (nonatomic, strong)NSMutableArray *listArr;

//盛放所有model的數(shù)組

@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文件中看見的那些方法的實現(xiàn)了立美,在viewModel中對請求的數(shù)據(jù)或者本地的數(shù)據(jù)處理之后匿又,返回給外部使用(這里說的不專業(yè)了,明白這個道理就好&—— &)

#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 *)getSectionModel

WithSection:(NSInteger)section { ? ?SectionModel *sModel = [self.listArr objectAtIndex:section];

return sModel;}

這個坑就是在這兒悯辙,想實現(xiàn)的效果是琳省,當我點擊cell的時候,我替換這個cell對應(yīng)的model數(shù)據(jù)躲撰,一開始是用的被注釋掉的方法针贬,這個稍微有些經(jīng)驗的都能想到這個不行,可是我就是那個掉坑的拢蛋,本來想著我找到對應(yīng)的section的model桦他,在sectionModel中找到對應(yīng)的RowModel然后將新model替換掉舊的。perfect谆棱。快压。。運行之后發(fā)現(xiàn)是行不通的垃瞧,然后用下面在數(shù)組中遍歷查找的方法進行替換解決的蔫劣。關(guān)于這問題我的理解是, 數(shù)組中存放的是model的指針个从,我用newModel替換oldModel脉幢,替換的只是model的指針,但是數(shù)組中儲存model的指針沒有改變嗦锐,所以數(shù)組并不會改變它保存的對應(yīng)位置的指針嫌松,所以說數(shù)組中對應(yīng)位置儲存的還是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"; ? ? ? ? ? ? ? ?} ? ? ? ? ? ?} ? ? ? ?} ? ?}}

關(guān)于model還是我們以前用的

@interface?SectionModel?:?NSObject

@property?(nonatomic,?copy)?NSString?*sectionName;

@property (nonatomic, strong) NSArray *rowModelArr;

@end

總結(jié)

寫這篇主要是記錄一個最基本的mvvm思想的實現(xiàn)還有一個以后絕對不能再犯的錯誤,遇到問題多想想碳默,別浮躁贾陷。晚安,明天還要上班嘱根,還有bug等我拯救昵宇。。儿子。

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末瓦哎,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子柔逼,更是在濱河造成了極大的恐慌蒋譬,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,470評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件愉适,死亡現(xiàn)場離奇詭異犯助,居然都是意外死亡,警方通過查閱死者的電腦和手機维咸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,393評論 3 392
  • 文/潘曉璐 我一進店門剂买,熙熙樓的掌柜王于貴愁眉苦臉地迎上來惠爽,“玉大人,你說我怎么就攤上這事瞬哼』樗粒” “怎么了?”我有些...
    開封第一講書人閱讀 162,577評論 0 353
  • 文/不壞的土叔 我叫張陵坐慰,是天一觀的道長较性。 經(jīng)常有香客問我,道長结胀,這世上最難降的妖魔是什么赞咙? 我笑而不...
    開封第一講書人閱讀 58,176評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮糟港,結(jié)果婚禮上攀操,老公的妹妹穿的比我還像新娘。我一直安慰自己秸抚,他們只是感情好崔赌,可當我...
    茶點故事閱讀 67,189評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著耸别,像睡著了一般健芭。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上秀姐,一...
    開封第一講書人閱讀 51,155評論 1 299
  • 那天慈迈,我揣著相機與錄音,去河邊找鬼省有。 笑死痒留,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的蠢沿。 我是一名探鬼主播伸头,決...
    沈念sama閱讀 40,041評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼舷蟀!你這毒婦竟也來了恤磷?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,903評論 0 274
  • 序言:老撾萬榮一對情侶失蹤野宜,失蹤者是張志新(化名)和其女友劉穎扫步,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體匈子,經(jīng)...
    沈念sama閱讀 45,319評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡河胎,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,539評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了虎敦。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片游岳。...
    茶點故事閱讀 39,703評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡政敢,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出胚迫,到底是詐尸還是另有隱情喷户,我是刑警寧澤,帶...
    沈念sama閱讀 35,417評論 5 343
  • 正文 年R本政府宣布晌区,位于F島的核電站,受9級特大地震影響通贞,放射性物質(zhì)發(fā)生泄漏朗若。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,013評論 3 325
  • 文/蒙蒙 一昌罩、第九天 我趴在偏房一處隱蔽的房頂上張望哭懈。 院中可真熱鬧,春花似錦茎用、人聲如沸遣总。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,664評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽旭斥。三九已至,卻和暖如春古涧,著一層夾襖步出監(jiān)牢的瞬間垂券,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,818評論 1 269
  • 我被黑心中介騙來泰國打工羡滑, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留菇爪,地道東北人。 一個月前我還...
    沈念sama閱讀 47,711評論 2 368
  • 正文 我出身青樓柒昏,卻偏偏與公主長得像凳宙,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子职祷,可洞房花燭夜當晚...
    茶點故事閱讀 44,601評論 2 353

推薦閱讀更多精彩內(nèi)容