A data-driven ASCollectionNode and ASTableNode wrapper for building fast and flexible lists.
- Smooth asynchronous user interfaces due to texture backing
- Never need call
performBatchUpdates(_:, completion:)
orreloadData()
again - Manage section easily
- Create lists with multiple data types
@interface ViewController ()
@property (nonatomic) HHTableNodeWrapper *listWrapper; // the list wrapper
@property (nonatomic) HHSectionController *sectionController; // the binded section controller
// saved section which will change dynamicly
@property (nonatomic) HHSectionModel *middleSection;
@property (nonatomic) HHSectionModel *walletSection;
// saved model that will change dynamicly
@property (nonatomic) HHSettingCommonModel *walletModel;
@end
@implementation ViewController
- (instancetype)init {
self = [super initWithNode:[ASDisplayNode new]];
if (self) {
_sectionController = [HHSectionController new];
_listWrapper = [[HHTableNodeWrapper alloc] initWithSectionController:self.sectionController
containingViewController:self
tableDelegate:nil
tableDataSource:nil];
[self.listWrapper enableAutoupdateWithAutoupdatingAnimated:NO]; // enable autoupdate
}
return self;
}
- (void)viewDidLoad {
[super viewDidLoad];
// add the wrapped list node to supernode
[self.node addSubnode:self.listWrapper.tableNode];
}
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
// layout the wrapped list node
self.listWrapper.tableNode.frame = self.node.bounds;
}
@end
@implementation ViewController
// ...
- (void)viewDidLoad {
// ...
[self configureSections];
// ...
}
- (void)configureSections {
HHSectionModel *avatarSection = ({
HHSectionModel *section = [HHSectionModel sectionModelWithCellNodeCreatorBlock:^HHCellNode *(SettingViewController *containingVC, id<HHCellNodeModelProtocol> model) {
return [HHSettingAvatarCellNode new];
} cellNodeTapAction:^(SettingViewController *containingVC, id<HHCellNodeModelProtocol> model) {
NSLog(@"Avatar cell is tapped");
}];
[section configureWithHeaderHeight:15
footerHeight:20];
[section addPlaceholderModel]; // one model one cell, no model no cell
section;
});
self.walletSection = ({
HHSectionModel *section = [HHSectionModel new];
[section configureWithHeaderHeight:0
footerHeight:20];
self.walletModel = [[HHSettingCommonModel alloc] initWithName:@"钱包" iconName:@"wallet" tapAction:^{
NSLog(@"Wallet is tapped");
}];
[section appendNewModel:self.walletModel];
section;
});
self.middleSection = ({
HHSectionModel *section = [HHSectionModel new];
[section configureWithHeaderHeight:0
footerHeight:20];
[section appendNewModel:[[HHSettingCommonModel alloc] initWithName:@"收藏" iconName:@"collect" tapAction:^{
NSLog(@"Collect is tapped");
}]];
[section appendNewModel:[[HHSettingCommonModel alloc] initWithName:@"相册" iconName:@"album" tapAction:^{
NSLog(@"Album is tapped");
}]];
[section appendNewModel:[[HHSettingCommonModel alloc] initWithName:@"卡包" iconName:@"cardwallet" tapAction:^{
NSLog(@"Cardwallet is tapped");
}]];
[section appendNewModel:[[HHSettingCommonModel alloc] initWithName:@"表情" iconName:@"emoji" tapAction:^{
NSLog(@"Emoji is tapped");
}]];
section;
});
HHSectionModel *settingSection = ({
HHSectionModel *section = [HHSectionModel new];
[section configureWithHeaderHeight:0
footerHeight:20];
[section appendNewModel:[[HHSettingCommonModel alloc] initWithName:@"设置" iconName:@"setting" tapAction:^{
NSLog(@"Setting is tapped");
}]];
section;
});
[self.sectionController appendSectionModel:avatarSection];
[self.sectionController appendSectionModel:self.walletSection];
[self.sectionController appendSectionModel:self.middleSection];
[self.sectionController appendSectionModel:settingSection];
}
@end
Section model can be initialized with cell node creator block and cell tap action, then the cell model needn't implement HHCellNodeModelProtocol
. Otherwise the cell model has to implement HHCellNodeModelProtocol
@implementation HHSettingCommonModel
- (HHCellNodeBlock)cellNodeBlock {
return ^HHCellNode *(SettingViewController *containingVC) {
return [HHSettingCommonCellNode new];
};
}
- (HHCellNodeTapAction)cellNodeTapAction {
return ^(SettingViewController *containingVC) {
self.tapAction();
};
}
@end
It is very common to delete/insert/update cell dynamicly. It's easy to do this. But first, you should save the changed section model and cell model to a property. Then just insert/delete/update the corresponding cell model in that section model.
- (void)didTapAdd {
[self.middleSection appendNewModel:[[HHSettingCommonModel alloc] initWithName:@"收藏" iconName:@"collect" tapAction:^{
NSLog(@"Collect is tapped");
}]];
}
- (void)didTapRefresh {
self.walletModel.name = @"钱包updated";
[self.walletSection markModelNeedsReload:self.walletModel];
}
If autoupdate is enabled, insert/delete/update cell model will be synchronized to UI automaticaly. Except that updated model should be marked needs reload handly. Just like above.
If autoupdate is not enabled, performUpdate
should be called on the list wrapper. And updated model should be marked needs reload handly as well.
You can see the completed project in the Demo project.
- Add
pod 'HHListKit'
to your Podfile. - Run
pod install
orpod update
. - Import <HHListKit/HHListKit.h>.
- iOS 9.0+
HHListKit
is MIT-licensed.