阅读本文须要对ReactiveCocoa足够了解,也能够参阅 图解ReactiveCocoa基本函数
Cocoa Touch Framework无疑是一个很好的框架,特别是对动画的支持,在我接触过的框架中多是最好的(固然我接触的框架可能比较少),可是就UITableView来讲确实存在不少吐槽点,从我我的理解的角度作些分析,尝试去解决这些吐槽点,并给到的解决方案。git
枚举历来都是为了可扩展而存在的,UITableView中对UITableViewStyle的使用堪称滥用,先看看这个枚举的定义,枚举项的命名不够直观,源码的注释也得不到有效信息,github
typedef NS_ENUM(NSInteger, UITableViewStyle) { UITableViewStylePlain, // regular table view UITableViewStyleGrouped // preferences style table view };
再看看以下文档的说明,基本明确了设计者的本意,UITableViewStyle想要区分的是页眉或页脚(section headers or footers)是否浮动,接下来作个剖析:objective-c
case plain A plain table view. Any section headers or footers are displayed as inline separators and float when the table view is scrolled. case grouped A table view whose sections present distinct groups of rows. The section headers and footers do not float.
UITableView的初始化函数api
- (instancetype)initWithFrame:(CGRect)frame style:(UITableViewStyle)style; // must specify style at creation. -initWithFrame: calls this with UITableViewStylePlain
综上得出结论:UITableViewStyle是不应用。缓存
UITableViewCell存在好几个枚举的乱用,乱用表示不应用的时候用了。app
typedef NS_ENUM(NSInteger, UITableViewCellStyle) { UITableViewCellStyleDefault, // Simple cell with text label and optional image view (behavior of UITableViewCell in iPhoneOS 2.x) UITableViewCellStyleValue1, // Left aligned label on left and right aligned label on right with blue text (Used in Settings) UITableViewCellStyleValue2, // Right aligned label on left with blue text and left aligned label on right (Used in Phone/Contacts) UITableViewCellStyleSubtitle // Left aligned label on top and left aligned label on bottom with gray text (Used in iPod). };
UITableViewCell的初始化方法中一样也带上了UITableViewCellStyle,先看代码框架
// Designated initializer. If the cell can be reused, you must pass in a reuse identifier. You should use the same reuse identifier for all cells of the same form. - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(nullable NSString *)reuseIdentifier;
若是说UITableView设计者以为就只存在两种style,那么UITableViewCell设计中加入UITableViewCellStyle就显得彻底是乱用了。同样的道理,枚举历来就不是为了扩展而存在,UITableViewCell作为cell的基类,扩展是必须的,不可能全部的cell都长的跟UITableViewCellStyle中定义的几个枚举项所分类的彻底同样,因此这个设计是有多恶心啊。ide
再看看UITableViewCellStyle的各个枚举项的命名,简直是残暴啊,UITableViewCellStyleValue1,UITableViewCellStyleValue2这些是什么鬼哦,再看看注释,分别说明Used in Settings和Used in Phone/Contacts,这就很明显了,这些实现彻底就是系统组件用到了这样的实现,而后直接作为api开放出来的,并无作很好的抽象,在初始化函数中加入UITableViewCellStyle,污染了初始化函数,限制了扩展,往往在写一个UITableViewCell的子类时,老是有一种莫名的哀伤,UITableViewCellStyle作为参数存在惟一的做用就是多写了点代码,而后没有任何意义。这些cell style所表示的cell彻底应该经过子类化来实现的,因此UITableViewCellStyle的乱用是有点惨不忍睹的。函数
typedef NS_ENUM(NSInteger, UITableViewCellSeparatorStyle) { UITableViewCellSeparatorStyleNone, UITableViewCellSeparatorStyleSingleLine, UITableViewCellSeparatorStyleSingleLineEtched // This separator style is only supported for grouped style table views currently };
怎么说也不该该存在这样一个枚举,CellSeparatorStyle这里针对不一样的UITableViewStyle而设计的,不论是何种style,应该只须要isShowCellSeparatorLine这样一个BOOL值表示是否须要显示边框,若是是UITableViewStyleGrouped这种style,可能须要额外的一个isCellSeparatorLineEtched,若是根据前面的假设,页眉或页脚都是默认浮动的话,这样设计是很合理的。fetch
当一个枚举各项的命名过于诡异时,这个枚举的存在其实是要好好考虑下的,因此UITableViewCellSeparatorStyle也是典型的乱用。
typedef NS_ENUM(NSInteger, UITableViewCellSelectionStyle) { UITableViewCellSelectionStyleNone, UITableViewCellSelectionStyleBlue, UITableViewCellSelectionStyleGray, UITableViewCellSelectionStyleDefault NS_ENUM_AVAILABLE_IOS(7_0) };
UITableViewCellSelectionStyle想表示cell选中的样式,这里大概是经过这种方式来提升几种默认值,由于CellSelectionStyle仍是能够定制的,可是UITableViewCellSelectionStyleDefault放在最后UITableViewCellSelectionStyleNone放在最开始,到底谁是default哦;
typedef NS_ENUM(NSInteger, UITableViewCellFocusStyle) { UITableViewCellFocusStyleDefault, UITableViewCellFocusStyleCustom } NS_ENUM_AVAILABLE_IOS(9_0);
UITableViewCellFocusStyle这个枚举的存在难道仅仅是为了无病呻吟吗?
UITableViewDelegate,UITableViewDataSource,包括刚引入的UITableViewDataSourcePrefetching,这几个delegate的设计好像是缺乏了些设计,更像是为了解决问题而写代码,做为一个基础框架,实在是不可取的。
// Display customization - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath; - (void)tableView:(UITableView *)tableView willDisplayHeaderView:(UIView *)view forSection:(NSInteger)section NS_AVAILABLE_IOS(6_0); - (void)tableView:(UITableView *)tableView willDisplayFooterView:(UIView *)view forSection:(NSInteger)section NS_AVAILABLE_IOS(6_0); - (void)tableView:(UITableView *)tableView didEndDisplayingCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath*)indexPath NS_AVAILABLE_IOS(6_0); - (void)tableView:(UITableView *)tableView didEndDisplayingHeaderView:(UIView *)view forSection:(NSInteger)section NS_AVAILABLE_IOS(6_0); - (void)tableView:(UITableView *)tableView didEndDisplayingFooterView:(UIView *)view forSection:(NSInteger)section NS_AVAILABLE_IOS(6_0);
这几个委托函数,都是与Cell、页眉、页脚相关的,可是全都集中在UITableViewDelegate这个委托中,且命名都是相似,当一个protocol在定义时存在过多的@optional委托函数时,这个protocol的设计自己就是不合理的,应该拆分红更细的protocol,咱们应该时在必要的时候选择相应的protocol,而不是实现存在的@optional委托函数,而后UITableViewDelegate这个protocol自己全部的委托函数都是@optional,这是真的不合理,若是是咱们来设计Cell、页眉、页脚实际上都是应该UIView,且存在诸多共同点(参考UICollectionView的设计,Cell、页眉、页脚就存在一个共同的基类UICollectionReusableView),应该设计一个UIReusableView,(UICollectionReusableView也能够不须要了)其中存在以下方法,这些方法能够在子类中重写
- (void)willAppear; - (void)didAppear; - (void)willDisappear; - (void)didDisappear
且应该设计一个UIReusableViewDelegate,其包括以下委托函数
- (void)willAppear:(UIReusableView*)reusableView; - (void)didAppear:(UIReusableView*)reusableView; - (void)willDisappear:(UIReusableView*)reusableView; - (void)didDisappear:(UIReusableView*)reusableView;
UIReusableView存在UIReusableViewDelegate的一个delegate,前面所提到的那六个委托函数,实际上应该在Cell、页眉、页脚各自须要的时候实现UIReusableViewDelegate。
综上,UITableViewDelegate其实是过重了。
下面这些委托函数,实际上应该存在UITableViewDataSource中。页眉、页脚的数据源跟cell的数据源应该是平等的存在,不该该是说不经常使用了,我就放到UITableViewDelegate中,原本就应该放在UITableViewDataSource,没必要须用的能够optional修饰下也还说得过去。
// Variable height support - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath; - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section; - (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section; // Use the estimatedHeight methods to quickly calcuate guessed values which will allow for fast load times of the table. // If these methods are implemented, the above -tableView:heightForXXX calls will be deferred until views are ready to be displayed, so more expensive logic can be placed there. - (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath NS_AVAILABLE_IOS(7_0); - (CGFloat)tableView:(UITableView *)tableView estimatedHeightForHeaderInSection:(NSInteger)section NS_AVAILABLE_IOS(7_0); - (CGFloat)tableView:(UITableView *)tableView estimatedHeightForFooterInSection:(NSInteger)section NS_AVAILABLE_IOS(7_0); // Section header & footer information. Views are preferred over title should you decide to provide both - (nullable UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section; // custom view for header. will be adjusted to default or specified header height - (nullable UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section; // custom view for footer. will be adjusted to default or specified footer height
通过前面的梳理,那么UITableViewDataSource中应该包括如下这些函数
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView; - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section; - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath; - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath; - (nullable NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section; - (nullable UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section; - (CGFloat)tableView:(UITableView *)tableView estimatedHeightForHeaderInSection:(NSInteger)section; - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section; - (nullable NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section; - (nullable UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section; - (CGFloat)tableView:(UITableView *)tableView estimatedHeightForFooterInSection:(NSInteger)section; - (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section; ……
跟前面提到UITableViewDelegate设计之重一个道理,Cell、页眉、页脚的DataSource也是应该分开的,在须要的时候实现对应的DataSource,须要定义额外的一个枚举UIReusableViewType
typedef NS_ENUM(NSInteger, UIReusableViewType) { UIReusableViewTypeNone, UIReusableViewTypeHeader, UIReusableViewTypeFooter };
而后对页眉、页脚就有UIReusableViewDataSource,其中的委托函数以下:
- (nullable NSString *)reusableView:(UIReusableView*)reusableView reusableViewType:(UIReusableViewType)reusableViewType titleInSection:(NSInteger)section; - (nullable UIView *)reusableView:(UIReusableView*)reusableViewreusableViewType:(UIReusableViewType)reusableViewType viewInSection:(NSInteger)section; - (CGFloat)reusableView:(UIReusableView*)reusableView reusableViewType:(UIReusableViewType)reusableViewType estimatedHeightInSection:(NSInteger)section;
单独的针对cell,有UITableViewCellDataSource,其中的委托函数以下:
- (NSInteger)numberOfSections; - (NSInteger)numberOfRowsInSection:(NSInteger)section; - (UITableViewCell *)cellForRowAtIndexPath:(NSIndexPath *)indexPath; - (CGFloat)heightForRowAtIndexPath:(NSIndexPath *)indexPath;
至于UITableViewDataSourcePrefetching就不该该出现,为了优化滚动帧率,拆东墙补西墙之举。从开发者的角度,最简单的作法就是把整个的数据源给到,剩下的就应该是UITableView自身去实现了,数据都有了,想要什么预加载都是框架自身的事情了,减小对开发者的依赖,更是减小api的耦合度,对外暴露的接口越多越很差。
前面在吐槽的时候,往往会给出自认为更合理的设计,然而并无什么卵用,既有代码是没法修改的,那改造之路又在何方呢?不能改变既有代码,那么只能是将这么东西尽量的封装起来,Objective-C语言还提供了一个蛮有意思的编译期常量NS_UNAVAILABLE,能够在编译期禁用父类的方法,算是不完美中的彻底吧,咱们能够禁用掉一些不合理的类成员,来达到一个比较好的封装效果。
UITableView能够禁用被枚举污染的初始化函数,重写默认的initWithFrame初始化函数并默认设style为UITableViewStyleGrouped,参考类 LPDTableView暂时并无重写初始化函数,目前认为无伤大雅。
UITableViewCell没法禁用被枚举污染的初始化函数,由于重用时会调用到,参考类 LPDTableViewCell,选择无视UITableViewCellStyle,并将已存在的几种cellStyle都扩展成对应的子类,LPDTableViewDefaultCell,LPDTableViewValue1Cell,LPDTableViewValue2Cell,LPDTableViewSubtitleCell命名仍是保留一致,毕竟你们都已经习惯了这种丑。
既然没法改造既有的UITableView,能够从另一个侧面来解决。
引入MVVM的思想,为UITableView添加对应的ViewModel,有了ViewModel,则能够引入数据驱动的方式,当咱们须要为Cell、页眉、页脚提供DataSource时,只须要调用LPDTableViewModelProtocl中的方法就行了,接口的粒度已经比较细了,但可能不是最合理的组合,相关的函数都在下面:
- (nullable NSIndexPath *)indexPathForCellViewModel:(__kindof id<LPDTableCellViewModelProtocol>)cellViewModel; - (nullable __kindof id<LPDTableCellViewModelProtocol>)cellViewModelFromIndexPath:(NSIndexPath *)indexPath; - (NSInteger)sectionIndexForHeaderViewModel:(__kindof id<LPDTableHeaderFooterViewModelProtocol>)headerViewModel; - (nullable __kindof id<LPDTableHeaderFooterViewModelProtocol>)headerViewModelFromSection:(NSInteger)sectionIndex; - (NSInteger)sectionIndexForFooterViewModel:(__kindof id<LPDTableHeaderFooterViewModelProtocol>)footerViewModel; - (nullable __kindof id<LPDTableHeaderFooterViewModelProtocol>)footerViewModelFromSection:(NSInteger)sectionIndex; - (void)addCellViewModel:(__kindof id<LPDTableCellViewModelProtocol>)cellViewModel; - (void)addCellViewModel:(__kindof id<LPDTableCellViewModelProtocol>)cellViewModel withRowAnimation:(UITableViewRowAnimation)animation; - (void)addCellViewModel:(__kindof id<LPDTableCellViewModelProtocol>)cellViewModel toSection:(NSUInteger)sectionIndex; - (void)addCellViewModel:(__kindof id<LPDTableCellViewModelProtocol>)cellViewModel toSection:(NSUInteger)sectionIndex withRowAnimation:(UITableViewRowAnimation)animation; - (void)addCellViewModels:(NSArray<__kindof id<LPDTableCellViewModelProtocol>> *)cellViewModels; - (void)addCellViewModels:(NSArray<__kindof id<LPDTableCellViewModelProtocol>> *)cellViewModels withRowAnimation:(UITableViewRowAnimation)animation; - (void)addCellViewModels:(NSArray<__kindof id<LPDTableCellViewModelProtocol>> *)cellViewModels toSection:(NSUInteger)sectionIndex; - (void)addCellViewModels:(NSArray<__kindof id<LPDTableCellViewModelProtocol>> *)cellViewModels toSection:(NSUInteger)sectionIndex withRowAnimation:(UITableViewRowAnimation)animation; - (void)insertCellViewModel:(__kindof id<LPDTableCellViewModelProtocol>)cellViewModel atIndex:(NSUInteger)index; - (void)insertCellViewModel:(__kindof id<LPDTableCellViewModelProtocol>)cellViewModel atIndex:(NSUInteger)index withRowAnimation:(UITableViewRowAnimation)animation; - (void)insertCellViewModel:(__kindof id<LPDTableCellViewModelProtocol>)cellViewModel atIndex:(NSUInteger)index inSection:(NSUInteger)sectionIndex; - (void)insertCellViewModel:(__kindof id<LPDTableCellViewModelProtocol>)cellViewModel atIndex:(NSUInteger)index inSection:(NSUInteger)sectionIndex withRowAnimation:(UITableViewRowAnimation)animation; - (void)insertCellViewModels:(NSArray<__kindof id<LPDTableCellViewModelProtocol>> *)cellViewModels atIndex:(NSUInteger)index; - (void)insertCellViewModels:(NSArray<__kindof id<LPDTableCellViewModelProtocol>> *)cellViewModels atIndex:(NSUInteger)index withRowAnimation:(UITableViewRowAnimation)animation; - (void)insertCellViewModels:(NSArray<__kindof id<LPDTableCellViewModelProtocol>> *)cellViewModels atIndex:(NSUInteger)index inSection:(NSUInteger)sectionIndex; - (void)insertCellViewModels:(NSArray<__kindof id<LPDTableCellViewModelProtocol>> *)cellViewModels atIndex:(NSUInteger)index withAnimation:(UITableViewRowAnimation)animation; - (void)insertCellViewModels:(NSArray<__kindof id<LPDTableCellViewModelProtocol>> *)cellViewModels atIndex:(NSUInteger)index inSection:(NSUInteger)sectionIndex withRowAnimation:(UITableViewRowAnimation)animation; - (void)reloadCellViewModelAtIndex:(NSUInteger)index inSection:(NSInteger)sectionIndex; - (void)reloadCellViewModelAtIndex:(NSUInteger)index inSection:(NSInteger)sectionIndex withRowAnimation:(UITableViewRowAnimation)animation; - (void)reloadCellViewModelsAtRange:(NSRange)range inSection:(NSInteger)sectionIndex; - (void)reloadCellViewModelsAtRange:(NSRange)range inSection:(NSInteger)sectionIndex withRowAnimation:(UITableViewRowAnimation)animation; - (void)removeLastCellViewModel; - (void)removeLastCellViewModelWithRowAnimation:(UITableViewRowAnimation)animation; - (void)removeLastCellViewModelFromSection:(NSUInteger)sectionIndex; - (void)removeLastCellViewModelFromSection:(NSUInteger)sectionIndex withRowAnimation:(UITableViewRowAnimation)animation; - (void)removeCellViewModelAtIndex:(NSUInteger)index; - (void)removeCellViewModelAtIndex:(NSUInteger)index withRowAnimation:(UITableViewRowAnimation)animation; - (void)removeCellViewModelAtIndex:(NSUInteger)index fromSection:(NSUInteger)sectionIndex; - (void)removeCellViewModelAtIndex:(NSUInteger)index fromSection:(NSUInteger)sectionIndex withRowAnimation:(UITableViewRowAnimation)animation; - (void)replaceCellViewModels:(NSArray<__kindof id<LPDTableCellViewModelProtocol>> *)cellViewModels fromIndex:(NSUInteger)index; - (void)replaceCellViewModels:(NSArray<__kindof id<LPDTableCellViewModelProtocol>> *)cellViewModels fromIndex:(NSUInteger)index withRowAnimation:(UITableViewRowAnimation)animation; - (void)replaceCellViewModels:(NSArray<__kindof id<LPDTableCellViewModelProtocol>> *)cellViewModels fromIndex:(NSUInteger)index inSection:(NSUInteger)sectionIndex; - (void)replaceCellViewModels:(NSArray<__kindof id<LPDTableCellViewModelProtocol>> *)cellViewModels fromIndex:(NSUInteger)index inSection:(NSUInteger)sectionIndex withRowAnimation:(UITableViewRowAnimation)animation; - (void)addSectionViewModel:(id<LPDTableSectionViewModelProtocol>)sectionViewModel; - (void)addSectionViewModel:(id<LPDTableSectionViewModelProtocol>)sectionViewModel withRowAnimation:(UITableViewRowAnimation)animation; - (void)addSectionViewModel:(id<LPDTableSectionViewModelProtocol>)sectionViewModel withCellViewModels:(NSArray<__kindof id<LPDTableCellViewModelProtocol>> *)cellViewModels; - (void)addSectionViewModel:(id<LPDTableSectionViewModelProtocol>)sectionViewModel withCellViewModels:(NSArray<__kindof id<LPDTableCellViewModelProtocol>> *)cellViewModels withRowAnimation:(UITableViewRowAnimation)animation; - (void)insertSectionViewModel:(id<LPDTableSectionViewModelProtocol>)sectionViewModel atIndex:(NSUInteger)index; - (void)insertSectionViewModel:(id<LPDTableSectionViewModelProtocol>)sectionViewModel atIndex:(NSUInteger)index withRowAnimation:(UITableViewRowAnimation)animation; - (void)insertSectionViewModel:(id<LPDTableSectionViewModelProtocol>)sectionViewModel withCellViewModels:(NSArray<__kindof id<LPDTableCellViewModelProtocol>> *)cellViewModels atIndex:(NSUInteger)index; - (void)insertSectionViewModel:(id<LPDTableSectionViewModelProtocol>)sectionViewModel withCellViewModels:(NSArray<__kindof id<LPDTableCellViewModelProtocol>> *)cellViewModels atIndex:(NSUInteger)index withRowAnimation:(UITableViewRowAnimation)animation; - (void)reloadSectionAtIndex:(NSUInteger)index; - (void)reloadSectionAtIndex:(NSUInteger)index withRowAnimation:(UITableViewRowAnimation)animation; - (void)reloadSectionsAtRange:(NSRange)range; - (void)reloadSectionsAtRange:(NSRange)range withRowAnimation:(UITableViewRowAnimation)animation; - (void)removeSectionAtIndex:(NSUInteger)index; - (void)removeAllSections; - (void)removeSectionAtIndex:(NSUInteger)index withRowAnimation:(UITableViewRowAnimation)animation; - (void)removeAllSectionsWithRowAnimation:(UITableViewRowAnimation)animation; - (void)replaceSectionWithCellViewModels:(NSArray<__kindof id<LPDTableCellViewModelProtocol>> *)cellViewModels; - (void)replaceSectionWithCellViewModels:(NSArray<__kindof id<LPDTableCellViewModelProtocol>> *)cellViewModels withRowAnimation:(UITableViewRowAnimation)animation; - (void)replaceSectionWithCellViewModels:(NSArray<__kindof id<LPDTableCellViewModelProtocol>> *)cellViewModels atSection:(NSUInteger)sectionIndex; - (void)replaceSectionWithCellViewModels:(NSArray<__kindof id<LPDTableCellViewModelProtocol>> *)cellViewModels atSection:(NSUInteger)sectionIndex withRowAnimation:(UITableViewRowAnimation)animation;
引入ReactiveCocoa中的RACSignal,将UITableViewDelegate中的委函数都转成信号,当咱们须要实现某一个委托函数,只须要订阅对应的RACSignal便可,不订阅没有任何反作用。
@property (nonatomic, strong, readonly) RACSignal *willDisplayCellSignal; @property (nonatomic, strong, readonly) RACSignal *willDisplayHeaderViewSignal; @property (nonatomic, strong, readonly) RACSignal *willDisplayFooterViewSignal; @property (nonatomic, strong, readonly) RACSignal *didEndDisplayingCellSignal; @property (nonatomic, strong, readonly) RACSignal *didEndDisplayingHeaderViewSignal; @property (nonatomic, strong, readonly) RACSignal *didEndDisplayingFooterViewSignal; @property (nonatomic, strong, readonly) RACSignal *didHighlightRowAtIndexPathSignal; @property (nonatomic, strong, readonly) RACSignal *didUnhighlightRowAtIndexPathSignal; @property (nonatomic, strong, readonly) RACSignal *didSelectRowAtIndexPathSignal; @property (nonatomic, strong, readonly) RACSignal *didDeselectRowAtIndexPathSignal; @property (nonatomic, strong, readonly) RACSignal *willBeginEditingRowAtIndexPathSignal; @property (nonatomic, strong, readonly) RACSignal *didEndEditingRowAtIndexPathSignal;
Cell、页眉、页脚跟其ViewModel之间须要遵照约定好的命名规则,如此会自动匹配。另外Cell、页眉、页脚默认都是重用的,同一类型reuseIdentifier同样,重用相关的函数就都在 LPDTableViewFactory。这个类中了当咱们关心DataSource或者Delegate时,咱们只须要跟对应的ViewModel交互便可,将Cell、页眉、页脚解耦合。
这个protocol的实现类LPDTableSectionViewModel,只是在ViewModel层抽象出来,这样才好完善ViewModel层的实现,并不存在对应的SectionView。
cell,header,footer的viewmodel中都有对应的height字段,须要根据viewmodel的model字段在bindingTo:viewModel函数中设置height值,能够针对model作height的缓存。
-(void)reloadTable { if (self.datas && self.datas.count > 0) { NSMutableArray *cellViewModels = [NSMutableArray array]; for (LPDPostModel *model in self.datas) { LPDTablePostCellViewModel *cellViewModel = [[LPDTablePostCellViewModel alloc]initWithViewModel:self.tableViewModel]; cellViewModel.model = model; [cellViewModels addObject:cellViewModel]; } [self.tableViewModel replaceSectionWithCellViewModels:cellViewModels withRowAnimation:UITableViewRowAnimationTop]; }else{ [self.tableViewModel removeAllSections]; } }
LPDPostModel *model = [[LPDPostModel alloc]init]; model.userId = 111111; model.identifier = 1003131; model.title = @"First Chapter"; model.body = @"GitBook allows you to organize your book into chapters, each chapter is stored in a separate file like this one."; LPDTablePostCellViewModel *cellViewModel = [[LPDTablePostCellViewModel alloc]initWithViewModel:self.tableViewModel]; cellViewModel.model = model; [self.tableViewModel insertCellViewModel:cellViewModel atIndex:0 withRowAnimation:UITableViewRowAnimationLeft];
NSMutableArray *cellViewModels = [NSMutableArray array]; LPDTableDefaultCellViewModel *cellViewModel1 = [[LPDTableDefaultCellViewModel alloc] initWithViewModel:self.tableViewModel]; cellViewModel1.text = @"芬兰没法"; cellViewModel1.detail = @"蜂王浆发了"; cellViewModel1.image = [UIImage imageNamed:@"01"]; [cellViewModels addObject:cellViewModel1]; LPDTableValue1CellViewModel *cellViewModel2 = [[LPDTableValue1CellViewModel alloc] initWithViewModel:self.tableViewModel]; cellViewModel2.text = @"芬兰没法"; cellViewModel2.detail = @"蜂王浆发了"; cellViewModel2.image = [UIImage imageNamed:@"02"]; [cellViewModels addObject:cellViewModel2]; LPDTableValue2CellViewModel *cellViewModel3 = [[LPDTableValue2CellViewModel alloc] initWithViewModel:self.tableViewModel]; cellViewModel3.text = @"芬兰没法"; cellViewModel3.detail = @"蜂王浆发了"; [cellViewModels addObject:cellViewModel3]; LPDTableSubtitleCellViewModel *cellViewModel4 = [[LPDTableSubtitleCellViewModel alloc] initWithViewModel:self.tableViewModel]; cellViewModel4.text = @"芬兰没法"; cellViewModel4.detail = @"蜂王浆发了"; cellViewModel4.image = [UIImage imageNamed:@"03"]; [cellViewModels addObject:cellViewModel4]; [self.tableViewModel insertCellViewModels:cellViewModels atIndex:0 withRowAnimation:UITableViewRowAnimationLeft];
[self.tableViewModel removeCellViewModelAtIndex:0 withRowAnimation:UITableViewRowAnimationRight];
[[[self.waybillsTableViewModel.didSelectRowAtIndexPathSignal deliverOnMainThread] takeUntil:[self rac_willDeallocSignal]] subscribeNext:^(RACTuple *tuple) { @strongify(self); __kindof id<LPDTableCellViewModelProtocol> cellViewModel = tuple.second; LPDWaybillModel *waybillModel = cellViewModel.model; if (waybillModel.cancelCode == 0) { LPDWaybillDetailViewModel *detailViewModel = [[LPDWaybillDetailViewModel alloc] init]; detailViewModel.waybillId = waybillModel.waybillId; [self.navigation pushViewModel:detailViewModel animated:YES]; } }];
具体请下载LPDTableviewKit,看看其中的demo。