A TreeView Template that you can make deep customization,With the two tree templates provided, you can accomplish most of your business needs。node
这是一个能够进行深度自定义的树形结构模板,经过提供的两个树形模板,基本能够完成大部分业务需求。git
一种是向下无限展开式的树形结构(ExpansionStyle
),另外一种是面包屑形式的树形结构(BreadcrumbsStyle
)。github
nodeModel
节点模型,自定义nodeView
节点视图,自定义node
节点的高度本质上无需继承,任意模型与视图均可以拿来构成一颗树,只要遵照相对应的NodeModelProtocol
和NodeViewProtocol
协议,本身实现相对应的属性与方法便可,固然,也能够直接继承模板提供的节点模型基类,或者直接继承协议,自定义一个新的协议。数组
分别对应本地数据源与网络数据源,同时能够指定树的展开动画RowAnimation
。建议使用手动刷新,这也是默认方式。bash
在nodeView
高度发生变化或者设置了缩进,会自动递归的向全部的subview
发送setNeedLayout
消息,能够在须要从新布局的子视图中重写layoutSubviews
进行从新布局。网络
BaseTreeNode
提供的一些辅助功能:TreeViewTemplate
文件夹拖入项目pod 'TreeViewTemplate'
(待完善)NodeTreeView
/**
初始化方法
@param frame frame
@param style 展示形式:BreadcrumbsStyle或者ExpansionStyle
@return treeView实例
*/
- (instancetype _Nullable )initWithFrame:(CGRect)frame
treeViewStyle:(NodeTreeViewStyle)style;
复制代码
@protocol NodeTreeViewDelegate
@required
/**
返回对应节点下的视图:视图能够遵循NodeViewProtocol协议,让view具备一些统一的行为>
一种node对应一种nodeView
@param node node节点
@return node视图
*/
- (id<NodeViewProtocol>_Nonnull)nodeTreeView:(NodeTreeView *_Nonnull)treeView viewForNode:(id<NodeModelProtocol>_Nonnull)node;
@optional
/**
返回对应级别下的缩进
@param treeView treeView
@param nodeLevel 对应的nodeLevel
@return 该level下对应的缩进
*/
- (CGFloat)nodeTreeView:(NodeTreeView *_Nonnull)treeView indentAtNodeLevel:(NSInteger)nodeLevel;
/**
点击事件回调
@param treeView 树
@param node 节点模型
*/
- (void)nodeTreeView:(NodeTreeView *_Nonnull)treeView didSelectNode:(id<NodeModelProtocol>_Nonnull)node;
@end
复制代码
/**
树的刷新策略
默认是手动刷新:NodeTreeRefreshPolicyManaul
*/
@property (nonatomic, assign) NodeTreeRefreshPolicy refreshPolicy;
复制代码
/**
刷新node节点对应的树
*/
- (void)reloadTreeViewWithNode:(id<NodeModelProtocol>_Nonnull)node;
/**
刷新node节点对应的树,能够指定动画展开的方式
@param node node节点
*/
- (void)reloadTreeViewWithNode:(id<NodeModelProtocol>_Nonnull)node
RowAnimation:(UITableViewRowAnimation)animation;
复制代码
NodeModelProtocol
节点模型协议NodeViewProtocol
节点视图协议@protocol NodeViewProtocol
@required
/**
更新单个Node行
@param node node模型
*/
- (void)updateNodeViewWithNodeModel:(id<NodeModelProtocol>)node;
/**
须要在该方法中,对view进行从新布局,为了处理在缩进的时候宽度变化形成的影响
*/
- (void)layoutSubviews;
@end
复制代码
在处理树的时候,用到的一些递归操做:ide
==================== NodeTreeView.m中对递归的使用 ====================
一、#pragma mark NodeTreeViewStyleExpansion模式下,初始化数据源,递归的将全部须要展开的节点,加入到初始数据源中
static inline void RecursiveInitializeAllNodesWithRootNode(NSMutableArray *allNodes,id<NodeModelProtocol>rootNode){
if (rootNode.expand == NO || rootNode.subNodes.count == 0) {
return;
}else{
if (allNodes.count == 0) {
[allNodes addObjectsFromArray:rootNode.subNodes];
}else{
NSUInteger beginPosition = [allNodes indexOfObject:rootNode] + 1;
NSUInteger endPosition = beginPosition + rootNode.subNodes.count - 1;
NSRange range = NSMakeRange(beginPosition, endPosition-beginPosition+1);
NSIndexSet *set = [NSIndexSet indexSetWithIndexesInRange:range];
[allNodes insertObjects:rootNode.subNodes atIndexes:set];
}
for (id<NodeModelProtocol>subNode in rootNode.subNodes) {
rootNode = subNode;
RecursiveInitializeAllNodesWithRootNode(allNodes, rootNode);
}
}
}
二、#pragma mark 递归的将某节点下全部子节点的展开状态置为NO
static inline void RecursiveFoldAllSubnodesAtNode(id<NodeModelProtocol>node){
if (node.subNodes.count>0) {
[node.subNodes enumerateObjectsUsingBlock:^(id<NodeModelProtocol> _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
if (obj.isExpand) {
obj.expand = NO;
RecursiveFoldAllSubnodesAtNode(node);
}
}];
}else{
return;
}
}
三、#pragma mark 递归的向view的全部子view发送setNeedsLayout消息,进行从新布局
static inline void RecursiveLayoutSubviews(UIView *_Nonnull view){
if (view.subviews.count == 0) {
return;
}else{
[view.subviews enumerateObjectsUsingBlock:^(__kindof UIView * _Nonnull subView, NSUInteger idx, BOOL * _Nonnull stop) {
[subView setNeedsLayout];
RecursiveLayoutSubviews(subView);
}];
}
}
==================== BaseTreeNode.m中对递归的使用 ====================
CGFloat treeHeight;
CGFloat tempNodeLevel;
四、#pragma mark 获取根节点
static inline id<NodeModelProtocol>RecursiveGetRootNodeWithNode(id<NodeModelProtocol> node){
if (node.fatherNode == node) {
node.expand = YES;
return node;
}else{
node = node.fatherNode;
tempNodeLevel = tempNodeLevel+1;
return RecursiveGetRootNodeWithNode(node);
}
}
五、#pragma mark 根据根节点获取树的高度
static inline void RecursiveCalculateTreeHeightWithRootNode(id<NodeModelProtocol> rootNode){
if (rootNode.subNodes.count == 0||!rootNode.isExpand) {
return ;
}
if (!isnan(rootNode.subTreeHeight)) {
treeHeight += rootNode.subTreeHeight;
}
for (id<NodeModelProtocol>obj in rootNode.subNodes) {
RecursiveCalculateTreeHeightWithRootNode(obj);
}
}
复制代码
一、面包屑模式-自动布局
二、面包屑模式-手动动画
三、Expansion模式-自动ui
四、Expansion模式-手动
GitHub下载地址:TreeViewTemplate