TestsTested | ✗ |
LangLanguage | Obj-CObjective C |
License | MIT |
ReleasedLast Release | Dec 2017 |
Maintained by qiaomu, halfrost, Assuner-Lee, EyreFree.
Depends on: | |
ReactiveObjC | >= 0 |
Masonry | >= 0 |
LPDAdditionsKit | >= 0 |
To run the example project, clone the repo, and run pod install
from the Example directory first.
对ReactiveCocoa足够了解,也可以参阅图解ReactiveCocoa基本函数
LPDTableViewKit is available through CocoaPods. To install it, simply add the following line to your Podfile:
pod "LPDTableViewKit"
foxsofter, [email protected]
LPDTableViewKit is available under the MIT license. See the LICENSE file for more info.
##UITableView改造之路
Cocoa Touch Framework无疑是一个很好的框架,特别是对动画的支持,在我接触过的框架中可能是最好的(当然我接触的框架可能比较少),但是就UITableView来说确实存在很多吐槽点,从我个人理解的角度做些分析,尝试去解决这些吐槽点,并给到的解决方案。
枚举从来都是为了可扩展而存在的,UITableView中对UITableViewStyle的使用堪称滥用,先看看这个枚举的定义,枚举项的命名不够直观,源码的注释也得不到有效信息,
typedef NS_ENUM(NSInteger, UITableViewStyle) {
UITableViewStylePlain, // regular table view
UITableViewStyleGrouped // preferences style table view
};
再看看如下文档的说明,基本明确了设计者的本意,UITableViewStyle想要区分的是页眉或页脚(section headers or footers)是否浮动,接下来做个剖析:
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的初始化函数
- (instancetype)initWithFrame:(CGRect)frame style:(UITableViewStyle)style; // must specify style at creation. -initWithFrame: calls this with UITableViewStylePlain
综上得出结论:UITableViewStyle是不该用。
UITableViewCell存在好几个枚举的乱用,乱用表示不该用的时候用了。
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中定义的几个枚举项所分类的完全一样,所以这个设计是有多恶心啊。
再看看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,如果根据前面的假设,页眉或页脚都是默认浮动的话,这样设计是很合理的。
当一个枚举各项的命名过于诡异时,这个枚举的存在实际上是要好好考虑下的,所以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];
}
}];
具体请下载lpd-tableview-kit,看看其中的demo。