嚴(yán)肅的目錄
一救湖、前言
1.寫之前的廢話
二、MVVM和RAC理解
1.MVVM淺談
2.RAC淺談
3.兩者的結(jié)合運(yùn)用
三弊予、架構(gòu)部分
1.演示架構(gòu)詳解
2.基類說明
3.對(duì)上述吧啦吧啦幾句
四悦析、實(shí)戰(zhàn)部分
1.ViewController的處理
2.View的處理
3.Model的處理
4.ViewModel的處理
五、后記
1.程序員話癆模式開啟
六司光、基情感謝
1.程序員之間的滿滿基情
一琅坡、寫之前吧啦吧啦幾句
經(jīng)過第一篇文章的多愁善感,接下來是表演真正的絕技的時(shí)候了残家,回歸我的老本行榆俺,宇宙無敵iOS軟件開發(fā)工程師!(裝逼完畢坞淮,此處可以有點(diǎn)掌聲)最近的IT行情不太好了茴晋,很多小獅子們都找不到工作了,面試的時(shí)候回窘,也會(huì)經(jīng)常被問到MVC等設(shè)計(jì)模式和架構(gòu)诺擅,今天我就談?wù)勛约旱睦斫猓?dāng)然了啡直,我也是剛從小白變成小白發(fā)點(diǎn)黑烁涌,剛接手的公司的項(xiàng)目用到的是MVVM+RAC的框架,現(xiàn)在終于有那么點(diǎn)滴的了解了酒觅,希望能幫助你那么一點(diǎn)撮执,就是我的初衷了。設(shè)計(jì)模式這東西舷丹,說簡(jiǎn)單也難抒钱,說難也不難,嘎嘎嘎,總之萬(wàn)變不離其宗谋币,用到MVVM我們的目標(biāo)就是仗扬,讓VC這個(gè)胖子減減肥,代碼看起來顏值高點(diǎn)瑞信,維護(hù)的時(shí)候方便點(diǎn)厉颤,數(shù)據(jù)處理更清晰點(diǎn)穴豫,耦合度低一點(diǎn)凡简,可復(fù)用性高點(diǎn),獨(dú)立開發(fā)更簡(jiǎn)單點(diǎn).....也就是那么一點(diǎn)優(yōu)點(diǎn)啦精肃。廢話不多說秤涩,開始ing。
二司抱、MVVM和RAC理解
1.MVVM淺談
MVC是構(gòu)建iOS App的標(biāo)準(zhǔn)模式筐眷,是蘋果推薦的一個(gè)用來組織代碼的權(quán)威范式,市面上大部分App都是這樣構(gòu)建的习柠,具體組建模式不細(xì)說匀谣,iOS入門者都比較了解(雖然不一定能完全去遵守),但其幾個(gè)不能避免的問題卻是很嚴(yán)重困擾開發(fā)者比如厚重的ViewController资溃、遺失的網(wǎng)絡(luò)邏輯(沒有屬于它的位置)武翎、較差的可測(cè)試性等因此也就會(huì)有維護(hù)性較強(qiáng)、耦合性很低的一種新架構(gòu)MVVM (MVC 引申出得新的架構(gòu))的流行溶锭。
MVVM雖然來自微軟宝恶,但是不應(yīng)該反對(duì)它,它正式規(guī)范了正式規(guī)范了視圖和控制器緊耦合的性質(zhì)趴捅,如下圖:
寫到這里垫毙,我看見下面有同學(xué)??說,ViewModel是什么鬼拱绑?
View綁定到ViewModel综芥,然后執(zhí)行一些命令在向它請(qǐng)求一個(gè)動(dòng)作。而反過來猎拨,ViewModel跟Model通訊膀藐,告訴它更新來響應(yīng)UI。這樣便使得應(yīng)用構(gòu)建UI非常的容易迟几。相比較于MVC新引入的視圖模型消请。是視圖顯示邏輯、驗(yàn)證邏輯类腮、網(wǎng)絡(luò)請(qǐng)求等代碼存放的地方臊泰,唯一要注意的是,任何視圖本身的引用都不應(yīng)該放在VM中蚜枢,換句話說就是VM中不要引入U(xiǎn)IKit.h缸逃。
這樣针饥,首先解決了VC臃腫的問題,將邏輯代碼需频、網(wǎng)絡(luò)請(qǐng)求等都寫入了VM中丁眼,然后又由于VM中包含了所有的展示邏輯而且不會(huì)引用V,所以它是可以通過編程充分測(cè)試的昭殉。
2.RAC淺談
ReactiveCocoa是響應(yīng)式編程(FRP)在iOS中的一個(gè)實(shí)現(xiàn)框架苞七,它的開源地址為:https://github.com/ReactiveCocoa/ReactiveCocoa# ;在網(wǎng)上看了幾篇文章挪丢,感覺理論講了很多蹂风,但是代碼還是看不太懂,于是自己把它github文檔上的一些使用的經(jīng)典示例實(shí)現(xiàn)了一下乾蓬,項(xiàng)目中有需要時(shí)可以直接搬過去用惠啄,用的熟練了再讀源碼也比較容易理解。
RAC雖然最大的優(yōu)點(diǎn)是提供了一個(gè)單一的任内、統(tǒng)一的方法去處理異步的行為撵渡,包括delegate方法,blocks回調(diào),target-action機(jī)制,notifications和KVO.但是不要簡(jiǎn)單的只是單純的認(rèn)為他僅僅就是減少代碼復(fù)雜度,更好的配合MVVM而已死嗦,小伙子趋距,這樣你就小看它了。
它最大的與眾不同是提供了一種新的寫代碼的思維越走,**由于RAC將Cocoa中KVO棚品、UIKit event、delegate廊敌、selector等都增加了RAC支持铜跑,所以都不用去做很多跨函數(shù)的事。
這里我就不細(xì)說了骡澈,里面的東西還是很多滴锅纺,需要慢慢沉淀,這里我推薦一篇寫的比較好的教程肋殴,大家互相學(xué)習(xí)囤锉。http://www.reibang.com/p/87ef6720a096
3.兩者結(jié)合的應(yīng)用
在此次介紹中,會(huì)使用MVVM+RAC結(jié)合的方式护锤,搞定一個(gè)添加上拉加載及下拉刷新的列表官地,所以更多的詮釋MVVM思想,而不是RAC的邏輯鏈?zhǔn)讲僮鳎ㄟ@一點(diǎn)用登錄界面來寫更能體現(xiàn)YoY )烙懦,RAC在此扮演的更大一部分的角色是更好的解耦驱入,減少代碼復(fù)雜度,使代碼層次分明、邏輯清晰更便于維護(hù)升級(jí)亏较。
二莺褒、架構(gòu)部分
1.演示架構(gòu)詳解
廢話不說半句多,原諒我這么不愛說話雪情,啊啊啊啊遵岩,看圖!
Frameworks
存放系統(tǒng)庫(kù)的文件夾巡通,新建工程的時(shí)候自己創(chuàng)建一個(gè)這個(gè)名字的文件夾尘执,添加的系統(tǒng)庫(kù)就會(huì)乖乖進(jìn)來啦??
AppDelegate
誰(shuí)不知道這是干嘛的,你過來扁达,我保證請(qǐng)你吃肉
Cl0ass
工程的主體正卧,日常代碼就放到這里了
General : 通用類(文件夾項(xiàng)目移植過程中都不需要更改的就能直接使用的)Base : 基類 (整個(gè)框架的基類)
Categories : 公共擴(kuò)展類 (就是一些常用的類別,比如分享啊什么的)
Core : 公共核心類(一般存放個(gè)人信息跪解、接口API等)
Models : 公共Model (公用的一些數(shù)據(jù)模型)
Views : 公共View (封裝的一些常用的View)
Helpers : 工程的相關(guān)輔助類(比如類似數(shù)據(jù)請(qǐng)求、表單上傳签孔、網(wǎng)絡(luò)監(jiān)測(cè)等工具類)
** Macro :** 宏定義類
** Sections :** 各模塊的文件夾(一般而言叉讥,我們以人為單位,畢竟項(xiàng)目很多時(shí)候是多人開發(fā)饥追,每個(gè)人創(chuàng)建一個(gè)Sections互不影響图仓,里面就存放自己的所有代碼和頁(yè)面啦)
Vendors : 第三方的類庫(kù)/SDK,如UMeng但绕、WeiboSDK救崔、WeixinSDK等等。
Resource
這里放置的是工程所需的一些資源捏顺,如下
Fonts 字體
Images 圖片
Sounds 聲音
Videos 視頻
Pods
當(dāng)你開發(fā)iOS應(yīng)用時(shí)六孵,會(huì)經(jīng)常使用到很多第三方開源類庫(kù),比如JSONKit幅骄,AFNetWorking等等劫窒。可能某個(gè)類庫(kù)又用到其他類庫(kù)拆座,所以要使用它主巍,必須得另外下載其他類庫(kù),而其他類庫(kù)又用到其他類庫(kù)挪凑,“子子孫孫無窮盡也”孕索,反正在早期我是體會(huì)過這種痛苦,好心酸躏碳,手動(dòng)一個(gè)個(gè)去下載所需類庫(kù)是十分麻煩的搞旭。
還有另外一種常見情況是,你項(xiàng)目中用到的類庫(kù)有更新,你必須得重新下載新版本选脊,重新加入到項(xiàng)目中杭抠,十分麻煩。
CocoaPods就是幫你解決上面的問題的恳啥,話說這玩意應(yīng)該是iOS最常用最有名的類庫(kù)管理工具了偏灿,作為iOS程序員的我們,掌握CocoaPods的使用是必不可少的基本技能了钝的,至于這玩意該咋用翁垂?
嘎嘎嘎,如果你看的足夠細(xì)心硝桩,給你們點(diǎn)福利沿猜,這個(gè)網(wǎng)站可以學(xué)習(xí)哈,哎碗脊,我心軟 http://code4app.com/article/cocoapods-install-usage
2.基類詳解
這里著重講解一下VC啼肩,View和ViewModel的基類。
1.YDViewController
yd_addSubviews : 添加View到ViewController
yd_bindViewModel : 用來綁定V(VC)與VM
yd_layoutNavigation : 設(shè)置導(dǎo)航欄衙伶、分欄
yd_getNewData : 初次獲取數(shù)據(jù)的時(shí)候調(diào)用(不是特別必要)
2.YDView
yd_setupViews : 添加子View到主View
yd_bindViewModel : 綁定V與VM
yd_addReturnKeyBoard : 設(shè)置點(diǎn)擊空白鍵盤回收
3.YDViewModel
yc_initialize : 進(jìn)行一些邏輯綁定祈坠,網(wǎng)絡(luò)數(shù)據(jù)請(qǐng)求處理。
LSRefreshDataStatus 數(shù)據(jù)處理后需要進(jìn)行的操作標(biāo)識(shí)LSHeaderRefresh_HasMoreData 下拉還有更多數(shù)據(jù)
LSHeaderRefresh_HasNoMoreData 下拉沒有更多數(shù)據(jù)
LSFooterRefresh_HasMoreData 上拉還有更多數(shù)據(jù)
LSFooterRefresh_HasNoMoreData 上拉沒有更多數(shù)據(jù)
LSRefreshError 刷新出錯(cuò)
LSRefreshUI 僅僅刷新UI布局
3.對(duì)上述吧啦吧啦幾句
基類的作用是統(tǒng)一管理矢劲,統(tǒng)一風(fēng)格赦拘,便于編碼,有更多的額外的附加功能的話芬沉,建議使用Protocol 或 Category躺同,這樣移植性強(qiáng),便于管理與擴(kuò)展丸逸,不至于牽一發(fā)而動(dòng)全身蹋艺。
本篇基類核心是用VM來配置V(VC),并提供一些必須的Protocol方法來處理界面顯示椭员、邏輯车海,將代碼風(fēng)格規(guī)范化,各個(gè)部分的功能明朗化隘击,這樣侍芝,當(dāng)你需要寫什么,需要找什么埋同,需要更改什么的時(shí)候都會(huì)很明確這些代碼的位置州叠,邏輯更清晰,而不會(huì)浪費(fèi)更多的時(shí)間在思考應(yīng)該寫在哪凶赁,該去哪找咧栗,要改的地方在哪這種不該費(fèi)時(shí)間的問題上逆甜。
四、實(shí)戰(zhàn)部分
接下來致板,實(shí)現(xiàn)一個(gè)最基本的列表交煞,由于本人比較懶,用我小伙伴的demo講解下斟或。話不多說素征,半句多,嘎嘎嘎萝挤,啦啦啊御毅。
然后處理完后的目錄如下:
簡(jiǎn)單介紹一下:
ViewController****LSCircleListViewController : 界面主控制器,負(fù)責(zé)跳轉(zhuǎn)怜珍、Navgation端蛆、TabBar等
View****LSCircleListView : 界面主View,負(fù)責(zé)主要界面的顯示
LSCircleListHeaderView : 頭部Header酥泛,封裝的內(nèi)部含有一個(gè)CollectionView
LSCircleListCollectionCell : 頭部Header中的CollectionView自定義的Cell
LSCircleListSectionHeaderView : SectionView今豆,此界面不需復(fù)用,所以單純一個(gè)View即可揭璃,若需要復(fù)用需要TableViewHeaderFooterView
LSCircleListTableCell : 主TableView的Cell
ViewModel****LSCircleListViewModel : 界面主ViewModel
LSCircleListHeaderViewModel : 頭部Header對(duì)應(yīng)的ViewModel
LSCircleListCollectionCellViewModel : 頭部CollectionCell及TableViewCell的ViewModel(因?yàn)槎叩臄?shù)據(jù)結(jié)構(gòu)是一致的)
LSCircleListSectionHeaderViewModel : Section的ViewModel
Model****LSCircleListModel : 圈子的數(shù)據(jù)模型(header和tableViewCell數(shù)據(jù)結(jié)構(gòu)是一致的)
一個(gè)小小的界面這么多類...是不是難以接受了晚凿,淡定些,騷年瘦馍!你要想想把這些個(gè)東西都放在VC內(nèi)是個(gè)什么趕腳?也得好幾千行呢Sσ邸(有點(diǎn)夸張情组!不過也夠頭疼的),這么多類箩祥,這里著重講一下主VC院崇、主V、主VM袍祖、主M就ok底瓣,能詳細(xì)講明白MVVM之間是如何工作的就一通百通了。
1蕉陋、LSCircleListViewController的處理
//
// LSCircleListViewController.m
// ZhongShui
//
// Created by 王隆帥 on 16/3/10.
// Copyright ? 2016年 王隆帥. All rights reserved.
//
#import "LSCircleListViewController.h"
#import "LSCircleListView.h"
#import "LSCircleListViewModel.h"
#import "LSCircleMainPageViewController.h"
#import "LSCircleMainPageViewModel.h"
#import "LSCircleListCollectionCellViewModel.h"
#import "LSNewCircleListViewController.h"
@interface LSCircleListViewController ()
@property (nonatomic, strong) LSCircleListView *mainView;
@property (nonatomic, strong) LSCircleListViewModel *viewModel;
@end
@implementation LSCircleListViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
}
#pragma mark - system
- (void)updateViewConstraints {
WS(weakSelf)
[self.mainView mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(weakSelf.view);
}];
[super updateViewConstraints];
}
#pragma mark - private
- (void)yc_addSubviews {
[self.view addSubview:self.mainView];
}
- (void)yc_bindViewModel {
@weakify(self);
[[self.viewModel.cellClickSubject takeUntil:self.rac_willDeallocSignal] subscribeNext:^(LSCircleListCollectionCellViewModel *viewModel) {
@strongify(self);
LSCircleMainPageViewModel *mainViewModel = [[LSCircleMainPageViewModel alloc] init];
mainViewModel.headerViewModel.circleId = viewModel.idStr;
mainViewModel.headerViewModel.headerImageStr = viewModel.headerImageStr;
mainViewModel.headerViewModel.title = viewModel.name;
mainViewModel.headerViewModel.numStr = viewModel.peopleNum;
LSCircleMainPageViewController *circleMainVC = [[LSCircleMainPageViewController alloc] initWithViewModel:mainViewModel];
[self.rdv_tabBarController setTabBarHidden:YES animated:YES];
[self.navigationController pushViewController:circleMainVC animated:YES];
}];
[self.viewModel.listHeaderViewModel.addNewSubject subscribeNext:^(id x) {
@strongify(self);
LSNewCircleListViewController *newCircleListVC = [[LSNewCircleListViewController alloc] init];
[self.rdv_tabBarController setTabBarHidden:YES animated:YES];
[self.navigationController pushViewController:newCircleListVC animated:YES];
}];
}
- (void)yc_layoutNavigation {
self.title = @"圈子列表";
[self.rdv_tabBarController setTabBarHidden:NO animated:YES];
}
#pragma mark - layzLoad
- (LSCircleListView *)mainView {
if (!_mainView) {
_mainView = [[LSCircleListView alloc] initWithViewModel:self.viewModel];
}
return _mainView;
}
- (LSCircleListViewModel *)viewModel {
if (!_viewModel) {
_viewModel = [[LSCircleListViewModel alloc] init];
}
return _viewModel;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
/*
#pragma mark - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
@end
對(duì)于VC捐凭,分為三個(gè)模塊页畦,下面分別來說一下:
i 第一個(gè)模塊:系統(tǒng)函數(shù)
此函數(shù)是從iOS6.0開始在ViewController中新增一個(gè)更新約束布局的方法御板,這個(gè)方法默認(rèn)的實(shí)現(xiàn)是調(diào)用對(duì)應(yīng)View的updateConstraints
。ViewController的View在更新視圖布局時(shí)御蒲,會(huì)先調(diào)用ViewController的updateViewConstraints 方法缩举。我們可以通過重寫這個(gè)方法去更新當(dāng)前View的內(nèi)部布局垦梆,而不用再繼承這個(gè)View去重寫-updateConstraints方法匹颤。我們?cè)谥貙戇@個(gè)方法時(shí),務(wù)必要調(diào)用 super 或者 調(diào)用當(dāng)前View的 -updateConstraints 方法托猩。
ⅱ 第二個(gè)模塊 : 私有函數(shù)
前面基類內(nèi)也提到了這三個(gè)函數(shù)的具體作用印蓖,即
yd_addSubviews : 添加View到ViewController
yd_bindViewModel : 這里綁定了兩個(gè)跳轉(zhuǎn)事件。
yd_layoutNavigation : 設(shè)置了標(biāo)題為“圈子列表”京腥、及TabBar不隱藏
ⅲ 第三個(gè)模塊 : 懶加載
2赦肃、View的處理
//
// LSCircleListView.m
// ZhongShui
//
// Created by 王隆帥 on 16/3/10.
// Copyright ? 2016年 王隆帥. All rights reserved.
//
#import "LSCircleListView.h"
#import "LSCircleListViewModel.h"
#import "LSCircleListHeaderView.h"
#import "LSCircleListSectionHeaderView.h"
#import "LSCircleListTableCell.h"
@interface LSCircleListView () <UITableViewDataSource, UITableViewDelegate>
@property (strong, nonatomic) LSCircleListViewModel *viewModel;
@property (strong, nonatomic) UITableView *mainTableView;
@property (strong, nonatomic) LSCircleListHeaderView *listHeaderView;
@property (strong, nonatomic) LSCircleListSectionHeaderView *sectionHeaderView;
@end
@implementation LSCircleListView
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect {
// Drawing code
}
*/
#pragma mark - system
- (instancetype)initWithViewModel:(id<YCViewModelProtocol>)viewModel {
self.viewModel = (LSCircleListViewModel *)viewModel;
return [super initWithViewModel:viewModel];
}
- (void)updateConstraints {
WS(weakSelf)
[self.mainTableView mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(weakSelf);
}];
[super updateConstraints];
}
#pragma mark - private
- (void)yc_setupViews {
[self addSubview:self.mainTableView];
[self setNeedsUpdateConstraints];
[self updateConstraintsIfNeeded];
}
- (void)yc_bindViewModel {
[self.viewModel.refreshDataCommand execute:nil];
@weakify(self);
[self.viewModel.refreshUI subscribeNext:^(id x) {
@strongify(self);
[self.mainTableView reloadData];
}];
[self.viewModel.refreshEndSubject subscribeNext:^(id x) {
@strongify(self);
[self.mainTableView reloadData];
switch ([x integerValue]) {
case LSHeaderRefresh_HasMoreData: {
[self.mainTableView.mj_header endRefreshing];
if (self.mainTableView.mj_footer == nil) {
self.mainTableView.mj_footer = [MJRefreshBackNormalFooter footerWithRefreshingBlock:^{
@strongify(self);
[self.viewModel.nextPageCommand execute:nil];
}];
}
}
break;
case LSHeaderRefresh_HasNoMoreData: {
[self.mainTableView.mj_header endRefreshing];
self.mainTableView.mj_footer = nil;
}
break;
case LSFooterRefresh_HasMoreData: {
[self.mainTableView.mj_header endRefreshing];
[self.mainTableView.mj_footer resetNoMoreData];
[self.mainTableView.mj_footer endRefreshing];
}
break;
case LSFooterRefresh_HasNoMoreData: {
[self.mainTableView.mj_header endRefreshing];
[self.mainTableView.mj_footer endRefreshingWithNoMoreData];
}
break;
case LSRefreshError: {
[self.mainTableView.mj_footer endRefreshing];
[self.mainTableView.mj_header endRefreshing];
}
break;
default:
break;
}
}];
}
#pragma mark - lazyLoad
- (LSCircleListViewModel *)viewModel {
if (!_viewModel) {
_viewModel = [[LSCircleListViewModel alloc] init];
}
return _viewModel;
}
- (UITableView *)mainTableView {
if (!_mainTableView) {
_mainTableView = [[UITableView alloc] init];
_mainTableView.delegate = self;
_mainTableView.dataSource = self;
_mainTableView.backgroundColor = GX_BGCOLOR;
_mainTableView.separatorStyle = UITableViewCellSeparatorStyleNone;
_mainTableView.tableHeaderView = self.listHeaderView;
[_mainTableView registerClass:[LSCircleListTableCell class] forCellReuseIdentifier:[NSString stringWithUTF8String:object_getClassName([LSCircleListTableCell class])]];
WS(weakSelf)
_mainTableView.mj_header = [MJRefreshNormalHeader headerWithRefreshingBlock:^{
[weakSelf.viewModel.refreshDataCommand execute:nil];
}];
_mainTableView.mj_footer = [MJRefreshAutoNormalFooter footerWithRefreshingBlock:^{
[weakSelf.viewModel.nextPageCommand execute:nil];
}];
}
return _mainTableView;
}
- (LSCircleListHeaderView *)listHeaderView {
if (!_listHeaderView) {
_listHeaderView = [[LSCircleListHeaderView alloc] initWithViewModel:self.viewModel.listHeaderViewModel];
_listHeaderView.frame = CGRectMake(0, 0, SCREEN_WIDTH, 160);
}
return _listHeaderView;
}
- (LSCircleListSectionHeaderView *)sectionHeaderView {
if (!_sectionHeaderView) {
_sectionHeaderView = [[LSCircleListSectionHeaderView alloc] initWithViewModel:self.viewModel.sectionHeaderViewModel];
}
return _sectionHeaderView;
}
#pragma mark - delegate
#pragma mark - UITableViewDataSource
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.viewModel.dataArray.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
LSCircleListTableCell *cell = [tableView dequeueReusableCellWithIdentifier:[NSString stringWithUTF8String:object_getClassName([LSCircleListTableCell class])] forIndexPath:indexPath];
if (self.viewModel.dataArray.count > indexPath.row) {
cell.viewModel = self.viewModel.dataArray[indexPath.row];
}
return cell;
}
#pragma mark - UITableViewDelegate
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return 100;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if (self.viewModel.dataArray.count > indexPath.row) {
[self.viewModel.cellClickSubject sendNext:self.viewModel.dataArray[indexPath.row]];
}
}
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
return self.sectionHeaderView;
}
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
return 45;
}
@end
3、LSCircleListModel的處理
//
// LSCircleListModel.h
// ZhongShui
//
// Created by 王隆帥 on 16/3/17.
// Copyright ? 2016年 王隆帥. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface LSCircleListModel : NSObject
@property (nonatomic, copy) NSString *idStr;
@property (nonatomic, copy) NSString *title;
@property (nonatomic, copy) NSString *intro;
@property (nonatomic, copy) NSString *img;
@property (nonatomic, copy) NSString *memberCount;
@property (nonatomic, copy) NSString *topicCount;
@end
#import "LSCircleListModel.h"
@implementation LSCircleListModel
+ (NSDictionary *)mj_replacedKeyFromPropertyName {
return @{
@"idStr":@"id",
@"title":@"title",
@"intro":@"intro",
@"img":@"img",
@"memberCount":@"MemberCount",
@"topicCount":@"TopicCount",
};
}
@end
4绞旅、ViewModel的處理
//
// LSCircleListViewModel.h
// ZhongShui
//
// Created by 王隆帥 on 16/3/10.
// Copyright ? 2016年 王隆帥. All rights reserved.
//
#import "YCViewModel.h"
#import "LSCircleListHeaderViewModel.h"
#import "LSCircleListSectionHeaderViewModel.h"
@interface LSCircleListViewModel : YCViewModel
@property (nonatomic, strong) RACSubject *refreshEndSubject;
@property (nonatomic, strong) RACSubject *refreshUI;
@property (nonatomic, strong) RACCommand *refreshDataCommand;
@property (nonatomic, strong) RACCommand *nextPageCommand;
@property (nonatomic, strong) LSCircleListHeaderViewModel *listHeaderViewModel;
@property (nonatomic, strong) LSCircleListSectionHeaderViewModel *sectionHeaderViewModel;
@property (nonatomic, strong) NSArray *dataArray;
@property (nonatomic, strong) RACSubject *cellClickSubject;
@end
//
// LSCircleListViewModel.m
// ZhongShui
//
// Created by 王隆帥 on 16/3/10.
// Copyright ? 2016年 王隆帥. All rights reserved.
//
#import "LSCircleListViewModel.h"
#import "LSCircleListCollectionCellViewModel.h"
#import "LSCircleListModel.h"
@interface LSCircleListViewModel ()
@property (nonatomic, assign) NSInteger currentPage;
@end
@implementation LSCircleListViewModel
- (void)yc_initialize {
@weakify(self);
[self.refreshDataCommand.executionSignals.switchToLatest subscribeNext:^(NSDictionary *dict) {
@strongify(self);
if (dict == nil) {
[self.refreshEndSubject sendNext:@(LSRefreshError)];
ShowErrorStatus(@"網(wǎng)絡(luò)連接失敗");
return;
}
if ([dict[@"status"] integerValue] == 0) {
self.listHeaderViewModel.dataArray = [[[([(NSDictionary *)dict[@"res"] arrayForKey:@"JoinCircles"]).rac_sequence map:^id(NSDictionary *dic) {
LSCircleListModel *model = [LSCircleListModel mj_objectWithKeyValues:dic];
LSCircleListCollectionCellViewModel *viewModel = [[LSCircleListCollectionCellViewModel alloc] init];
viewModel.model = model;
return viewModel;
}] array] mutableCopy];
self.dataArray = [[[([(NSDictionary *)dict[@"res"] arrayForKey:@"Circles"]).rac_sequence map:^id(NSDictionary *dic) {
LSCircleListModel *model = [LSCircleListModel mj_objectWithKeyValues:dic];
LSCircleListCollectionCellViewModel *viewModel = [[LSCircleListCollectionCellViewModel alloc] init];
viewModel.model = model;
return viewModel;
}] array] mutableCopy];
[self ls_setHeaderRefreshWithArray:dict[@"Circles"]];
[self ls_dismiss];
} else {
[self.refreshEndSubject sendNext:@(LSRefreshError)];
ShowMessage(dict[@"mes"]);
}
}];
[[[self.refreshDataCommand.executing skip:1] take:1] subscribeNext:^(id x) {
@strongify(self);
if ([x isEqualToNumber:@(YES)]) {
[self ls_showWithStatus:@"正在加載"];
}
}];
[self.nextPageCommand.executionSignals.switchToLatest subscribeNext:^(NSDictionary *dict) {
@strongify(self);
if (dict == nil) {
[self.refreshEndSubject sendNext:@(LSRefreshError)];
ShowErrorStatus(@"網(wǎng)絡(luò)連接失敗");
return;
}
if ([dict[@"status"] integerValue] == 0) {
NSMutableArray *recommandArray = [[NSMutableArray alloc] initWithArray:self.dataArray];
for (NSDictionary *subDic in [(NSDictionary *)dict[@"res"] arrayForKey:@"Circles"]) {
LSCircleListModel *model = [LSCircleListModel mj_objectWithKeyValues:subDic];
LSCircleListCollectionCellViewModel *viewModel = [[LSCircleListCollectionCellViewModel alloc] init];
viewModel.model = model;
[recommandArray addObject:viewModel];
}
self.dataArray = recommandArray;
[self ls_setFootRefreshWithArray:dict[@"Circles"]];
[self ls_dismiss];
} else {
[self.refreshEndSubject sendNext:@(LSRefreshError)];
ShowMessage(dict[@"mes"]);
}
}];
}
#pragma mark - private
- (NSMutableDictionary *)requestCircleListWithId:(NSString *)idStr currentPage:(NSString *)currentPage {
idStr = IF_NULL_TO_STRING(idStr);
currentPage = IF_NULL_TO_STRING(currentPage);
NSMutableDictionary * dict = [@{@"MemberID": idStr, @"pageSize": LS_REQUEST_LIST_COUNT, @"pageIndex":currentPage} mutableCopy];
return dict;
}
- (void)ls_setFootRefreshWithArray:(NSArray *)array {
if (array.count < LS_REQUEST_LIST_NUM_COUNT) {
[self.refreshEndSubject sendNext:@(LSFooterRefresh_HasNoMoreData)];
} else {
[self.refreshEndSubject sendNext:@(LSFooterRefresh_HasMoreData)];
}
}
- (void)ls_setHeaderRefreshWithArray:(NSArray *)array {
if (array.count < LS_REQUEST_LIST_NUM_COUNT) {
[self.refreshEndSubject sendNext:@(LSHeaderRefresh_HasNoMoreData)];
} else {
[self.refreshEndSubject sendNext:@(LSHeaderRefresh_HasMoreData)];
}
}
#pragma mark - lazyLoad
- (RACSubject *)refreshUI {
if (!_refreshUI) {
_refreshUI = [RACSubject subject];
}
return _refreshUI;
}
- (RACSubject *)refreshEndSubject {
if (!_refreshEndSubject) {
_refreshEndSubject = [RACSubject subject];
}
return _refreshEndSubject;
}
- (RACCommand *)refreshDataCommand {
if (!_refreshDataCommand) {
@weakify(self);
_refreshDataCommand = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) {
@strongify(self);
return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
@strongify(self);
self.currentPage = 1;
[self.request POST:LS_URL_CIRCLE_MEMBER_LIST parameters:[self requestCircleListWithId:@"1" currentPage:[NSString stringWithFormat:@"%d",self.currentPage]] success:^(CMRequest *request, NSString *responseString) {
NSDictionary *dict = [responseString objectFromJSONString];
[subscriber sendNext:dict];
[subscriber sendCompleted];
} failure:^(CMRequest *request, NSError *error) {
ShowErrorStatus(@"網(wǎng)絡(luò)連接失敗");
[subscriber sendCompleted];
}];
return nil;
}];
}];
}
return _refreshDataCommand;
}
- (RACCommand *)nextPageCommand {
if (!_nextPageCommand) {
@weakify(self);
_nextPageCommand = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) {
@strongify(self);
return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
@strongify(self);
self.currentPage ++;
[self.request POST:LS_URL_CIRCLE_TOPIC_LIST parameters:nil success:^(CMRequest *request, NSString *responseString) {
NSDictionary *dict = [responseString objectFromJSONString];
[subscriber sendNext:dict];
[subscriber sendCompleted];
} failure:^(CMRequest *request, NSError *error) {
@strongify(self);
self.currentPage --;
ShowErrorStatus(@"網(wǎng)絡(luò)連接失敗");
[subscriber sendCompleted];
}];
return nil;
}];
}];
}
return _nextPageCommand;
}
- (LSCircleListHeaderViewModel *)listHeaderViewModel {
if (!_listHeaderViewModel) {
_listHeaderViewModel = [[LSCircleListHeaderViewModel alloc] init];
_listHeaderViewModel.title = @"已加入的圈子";
_listHeaderViewModel.cellClickSubject = self.cellClickSubject;
}
return _listHeaderViewModel;
}
- (LSCircleListSectionHeaderViewModel *)sectionHeaderViewModel {
if (!_sectionHeaderViewModel) {
_sectionHeaderViewModel = [[LSCircleListSectionHeaderViewModel alloc] init];
_sectionHeaderViewModel.title = @"推薦圈子";
}
return _sectionHeaderViewModel;
}
- (NSArray *)dataArray {
if (!_dataArray) {
_dataArray = [[NSArray alloc] init];
}
return _dataArray;
}
- (RACSubject *)cellClickSubject {
if (!_cellClickSubject) {
_cellClickSubject = [RACSubject subject];
}
return _cellClickSubject;
}
@end
ViewModel也是分為三個(gè)模塊摆尝,由于代碼太多摘重要的講
ⅰ 第一個(gè)模塊 : 處理數(shù)據(jù)、邏輯模塊
處理數(shù)據(jù)這塊因悲,先用字典轉(zhuǎn)為Model堕汞,在用Model配置ViewModel,ViewModel再去與UI及其邏輯對(duì)應(yīng)晃琳。
ⅱ 第二個(gè)模塊 : 私有函數(shù)
對(duì)于請(qǐng)求參數(shù)字典讯检,可以放在VM中,便于模塊化移植卫旱,也可以放在公共API中便于管理人灼,看個(gè)人選擇了,沒有絕對(duì)的好位置顾翼,只有更適合個(gè)人的位置投放。
另外兩個(gè)函數(shù)就是處理下拉及上拉時(shí)有沒有更多數(shù)據(jù)的私有函數(shù)。
ⅲ 第三個(gè)模塊 : 懶加載
五适贸、后記
寫到這里灸芳,我這個(gè)究級(jí)話癆也是累了,沒錯(cuò)拜姿,這個(gè)設(shè)計(jì)模式就是話嘮標(biāo)準(zhǔn)模式外加裝逼標(biāo)準(zhǔn)模式烙样,但是,也是你不得不會(huì)的蕊肥。等你掌握后谒获,那開發(fā)速度真是蹭蹭的,你的邏輯思維就好像每天在算10以內(nèi)加減法壁却。批狱。話以至此,不懂得朋友們儒洛,歡迎百度和留言精耐,大家多多交流,我是很喜歡交朋友滴琅锻,咱們一起好好學(xué)習(xí)敲代碼,為了new出白富美卦停。
六向胡、滿滿的基情
感激我那迷人的小妖精~帥神的支持,由于本人比較懶惊完,用了帥神的截圖和點(diǎn)評(píng)僵芹,But,我們的目標(biāo)都是希望大家共同進(jìn)步小槐,在我們基情的滋潤(rùn)下茁壯成長(zhǎng)拇派,咳咳咳...希望大家多多留言,多交流凿跳。喜歡的朋友歡迎關(guān)注件豌,我會(huì)持續(xù)更新文章的,教你做一枚多愁善感的程序員控嗜,啊哈哈哈~親們茧彤,記得點(diǎn)贊哦