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