TreeViewTemplate移動組件詳細介紹

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

1、功能簡介

一、支持兩種常見的樹形結構

一種是向下無限展開式的樹形結構(ExpansionStyle),另外一種是麪包屑形式的樹形結構(BreadcrumbsStyle)。github

二、支持自定義nodeModel節點模型,自定義nodeView節點視圖,自定義node節點的高度

本質上無需繼承,任意模型與視圖均可以拿來構成一顆樹,只要遵照相對應的NodeModelProtocolNodeViewProtocol協議,本身實現相對應的屬性與方法便可,固然,也能夠直接繼承模板提供的節點模型基類,或者直接繼承協議,自定義一個新的協議。數組

三、支持縮進

四、支持自動刷新與手動刷新兩種方式

分別對應本地數據源與網絡數據源,同時能夠指定樹的展開動畫RowAnimation。建議使用手動刷新,這也是默認方式。bash

五、支持自動佈局

nodeView高度發生變化或者設置了縮進,會自動遞歸的向全部的subview發送setNeedLayout消息,能夠在須要從新佈局的子視圖中重寫layoutSubviews進行從新佈局。網絡

六、節點模型基類BaseTreeNode提供的一些輔助功能:

  • 自動遞歸計算樹的根節點
  • 自動遞歸計算樹的高度
  • 自動遞歸計算該節點所在的層級,默認根節點的層級爲0
  • 其餘基本操做

2、如何使用

安裝

  • 手動安裝:將TreeViewTemplate文件夾拖入項目
  • CocoaPod:podfile加入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;

複製代碼

五、使用時建議將treeView做爲一個cell,放在一個tableview中使用。

六、關於NodeModelProtocol節點模型協議

  • 聲明瞭一些遵照該協議的模型須要手動實現的屬性和方法。
  • 屬性聲明:節點高度、子節點數組、父節點、節點所在的層級、節點展開後的全部兒子節點的高度之和、該節點所在樹的當前總體高度、節點是否展開。
  • 方法聲明:增長節點、從數組中增長節點、刪除節點。

七、關於NodeViewProtocol節點視圖協議

  • 定義了全部節點視圖必須實現的方法,以下所示:
@protocol NodeViewProtocol
@required
/**
 更新單個Node行

 @param node node模型
 */
- (void)updateNodeViewWithNodeModel:(id<NodeModelProtocol>)node;
/**
 須要在該方法中,對view進行從新佈局,爲了處理在縮進的時候寬度變化形成的影響
 */
- (void)layoutSubviews;

@end
複製代碼

3、對遞歸的使用

在處理樹的時候,用到的一些遞歸操做: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);
    }
}

複製代碼

4、效果展現

一、麪包屑模式-自動佈局

麪包屑模式-自動

二、麪包屑模式-手動動畫

麪包屑模式-手動

三、Expansion模式-自動ui

Expansion模式-自動

四、Expansion模式-手動

Expansion模式-手動

GitHub下載地址:TreeViewTemplate

相關文章
相關標籤/搜索