├── WMZTreeView
├── Assets.xcassets
│ ├── Contents.json
│ ├── default_maintenance.imageset
│ │ ├── default_maintenance@3x.png
│ │ └── Contents.json
│ └── AppIcon.appiconset
│ │ └── Contents.json
├── WMZTreeView
│ ├── WMZTreeView.bundle
│ │ ├── close.png
│ │ ├── open.png
│ │ ├── treeCheck.png
│ │ ├── treeCheck1.png
│ │ ├── treeCheckSelect.png
│ │ └── treeHalfSelect.png
│ ├── WMZTreeParam.h
│ ├── WMZTreeProcotol.h
│ ├── WMZTreeCustomCell.h
│ ├── WMZTreeBaseView.h
│ ├── WMZTreeView.h
│ ├── WMZTreeParam.m
│ ├── WMZTreeConfig.h
│ ├── WMZTreeViewParam.m
│ ├── WMZTreeViewParam.h
│ ├── WMZTreeCustomCell.m
│ ├── WMZTreeBaseView.m
│ └── WMZTreeView.m
├── AppDelegate.h
├── DemoVC.h
├── ViewController.h
├── WMZMyCell.h
├── SceneDelegate.h
├── WMZCustomModel.h
├── main.m
├── WMZCustomModel.m
├── Base.lproj
│ ├── Main.storyboard
│ └── LaunchScreen.storyboard
├── AppDelegate.m
├── SceneDelegate.m
├── Info.plist
├── DemoVC.m
├── WMZMyCell.m
└── ViewController.m
├── WMZTreeView.xcodeproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ ├── xcuserdata
│ │ └── wmz.xcuserdatad
│ │ │ └── UserInterfaceState.xcuserstate
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
├── xcuserdata
│ └── wmz.xcuserdatad
│ │ ├── xcschemes
│ │ └── xcschememanagement.plist
│ │ └── xcdebugger
│ │ └── Breakpoints_v2.xcbkptlist
└── project.pbxproj
├── WMZTreeViewTests
├── Info.plist
└── WMZTreeViewTests.m
├── WMZTreeViewUITests
├── Info.plist
└── WMZTreeViewUITests.m
├── WMZTreeView.podspec
├── LICENSE
└── README.md
/WMZTreeView/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/WMZTreeView/WMZTreeView/WMZTreeView.bundle/close.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wwmz/WMZTreeView/HEAD/WMZTreeView/WMZTreeView/WMZTreeView.bundle/close.png
--------------------------------------------------------------------------------
/WMZTreeView/WMZTreeView/WMZTreeView.bundle/open.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wwmz/WMZTreeView/HEAD/WMZTreeView/WMZTreeView/WMZTreeView.bundle/open.png
--------------------------------------------------------------------------------
/WMZTreeView/WMZTreeView/WMZTreeView.bundle/treeCheck.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wwmz/WMZTreeView/HEAD/WMZTreeView/WMZTreeView/WMZTreeView.bundle/treeCheck.png
--------------------------------------------------------------------------------
/WMZTreeView/WMZTreeView/WMZTreeView.bundle/treeCheck1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wwmz/WMZTreeView/HEAD/WMZTreeView/WMZTreeView/WMZTreeView.bundle/treeCheck1.png
--------------------------------------------------------------------------------
/WMZTreeView/WMZTreeView/WMZTreeView.bundle/treeCheckSelect.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wwmz/WMZTreeView/HEAD/WMZTreeView/WMZTreeView/WMZTreeView.bundle/treeCheckSelect.png
--------------------------------------------------------------------------------
/WMZTreeView/WMZTreeView/WMZTreeView.bundle/treeHalfSelect.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wwmz/WMZTreeView/HEAD/WMZTreeView/WMZTreeView/WMZTreeView.bundle/treeHalfSelect.png
--------------------------------------------------------------------------------
/WMZTreeView/Assets.xcassets/default_maintenance.imageset/default_maintenance@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wwmz/WMZTreeView/HEAD/WMZTreeView/Assets.xcassets/default_maintenance.imageset/default_maintenance@3x.png
--------------------------------------------------------------------------------
/WMZTreeView.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/WMZTreeView.xcodeproj/project.xcworkspace/xcuserdata/wmz.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wwmz/WMZTreeView/HEAD/WMZTreeView.xcodeproj/project.xcworkspace/xcuserdata/wmz.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/WMZTreeView/AppDelegate.h:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.h
3 | // WMZTree
4 | //
5 | // Created by wmz on 2019/10/17.
6 | // Copyright © 2019 wmz. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface AppDelegate : UIResponder
12 |
13 | @end
14 |
15 |
--------------------------------------------------------------------------------
/WMZTreeView/DemoVC.h:
--------------------------------------------------------------------------------
1 | //
2 | // DemoVC.h
3 | // WMZTree
4 | //
5 | // Created by wmz on 2019/10/23.
6 | // Copyright © 2019 wmz. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | NS_ASSUME_NONNULL_BEGIN
12 |
13 | @interface DemoVC : UIViewController
14 |
15 | @end
16 |
17 | NS_ASSUME_NONNULL_END
18 |
--------------------------------------------------------------------------------
/WMZTreeView.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/WMZTreeView/ViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.h
3 | // WMZTree
4 | //
5 | // Created by wmz on 2019/10/17.
6 | // Copyright © 2019 wmz. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface ViewController : UIViewController
12 |
13 | @property(nonatomic,assign)NSInteger type;
14 |
15 | @end
16 |
17 |
--------------------------------------------------------------------------------
/WMZTreeView/WMZMyCell.h:
--------------------------------------------------------------------------------
1 | //
2 | // WMZMyCell.h
3 | // WMZTree
4 | //
5 | // Created by wmz on 2019/10/22.
6 | // Copyright © 2019 wmz. All rights reserved.
7 | //
8 |
9 | #import "WMZTreeCustomCell.h"
10 |
11 | NS_ASSUME_NONNULL_BEGIN
12 |
13 | @interface WMZMyCell : WMZTreeCustomCell
14 |
15 | @end
16 |
17 | NS_ASSUME_NONNULL_END
18 |
--------------------------------------------------------------------------------
/WMZTreeView/SceneDelegate.h:
--------------------------------------------------------------------------------
1 | //
2 | // SceneDelegate.h
3 | // WMZTree
4 | //
5 | // Created by wmz on 2019/10/17.
6 | // Copyright © 2019 wmz. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface SceneDelegate : UIResponder
12 |
13 | @property (strong, nonatomic) UIWindow * window;
14 |
15 | @end
16 |
17 |
--------------------------------------------------------------------------------
/WMZTreeView/WMZCustomModel.h:
--------------------------------------------------------------------------------
1 | //
2 | // WMZCustomModel.h
3 | // WMZTreeView
4 | //
5 | // Created by wmz on 2022/10/12.
6 | // Copyright © 2022 wmz. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "WMZTreeProcotol.h"
11 |
12 | NS_ASSUME_NONNULL_BEGIN
13 |
14 | @interface WMZCustomModel : NSObject
15 |
16 | @end
17 |
18 | NS_ASSUME_NONNULL_END
19 |
--------------------------------------------------------------------------------
/WMZTreeView/Assets.xcassets/default_maintenance.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "scale" : "2x"
10 | },
11 | {
12 | "idiom" : "universal",
13 | "filename" : "default_maintenance@3x.png",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/WMZTreeView.xcodeproj/xcuserdata/wmz.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | WMZTreeView.xcscheme_^#shared#^_
8 |
9 | orderHint
10 | 0
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/WMZTreeView/main.m:
--------------------------------------------------------------------------------
1 | //
2 | // main.m
3 | // WMZTreeView
4 | //
5 | // Created by wmz on 2019/10/24.
6 | // Copyright © 2019 wmz. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "AppDelegate.h"
11 |
12 | int main(int argc, char * argv[]) {
13 | NSString * appDelegateClassName;
14 | @autoreleasepool {
15 | // Setup code that might create autoreleased objects goes here.
16 | appDelegateClassName = NSStringFromClass([AppDelegate class]);
17 | }
18 | return UIApplicationMain(argc, argv, nil, appDelegateClassName);
19 | }
20 |
--------------------------------------------------------------------------------
/WMZTreeView/WMZCustomModel.m:
--------------------------------------------------------------------------------
1 | //
2 | // WMZCustomModel.m
3 | // WMZTreeView
4 | //
5 | // Created by wmz on 2022/10/12.
6 | // Copyright © 2022 wmz. All rights reserved.
7 | //
8 |
9 | #import "WMZCustomModel.h"
10 |
11 | @implementation WMZCustomModel
12 | @synthesize canSelect = _canSelect;
13 | @synthesize isExpand = _isExpand;
14 | @synthesize name = _name;
15 | @synthesize data = _data;
16 | @synthesize currentId = _currentId;
17 | @synthesize parentId = _parentId;
18 | @synthesize children = _children;
19 | @synthesize depath = _depath;
20 | @synthesize halfSelect = _halfSelect;
21 | @synthesize isSelected = _isSelected;
22 | @synthesize isTempSelected = _isTempSelected;
23 | @synthesize tempHalfSelect = _tempHalfSelect;
24 | @end
25 |
--------------------------------------------------------------------------------
/WMZTreeView.xcodeproj/xcuserdata/wmz.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
9 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/WMZTreeViewTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 |
22 |
23 |
--------------------------------------------------------------------------------
/WMZTreeViewUITests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 |
22 |
23 |
--------------------------------------------------------------------------------
/WMZTreeView/WMZTreeView/WMZTreeParam.h:
--------------------------------------------------------------------------------
1 | //
2 | // WMZTreeParam.h
3 | // WMZTree
4 | //
5 | // Created by wmz on 2019/10/17.
6 | // Copyright © 2019 wmz. All rights reserved.
7 | //
8 |
9 | #import "WMZTreeProcotol.h"
10 | NS_ASSUME_NONNULL_BEGIN
11 |
12 | @interface WMZTreeParam : NSObject
13 |
14 | WMZTreeParam * TreeParam(void);
15 | ///初始化方法
16 | - (instancetype)initWithName:(NSString*)name currentId:(NSString*)currentId parentId:(nullable NSString*)parentId;
17 |
18 | - (instancetype)initWithName:(NSString*)name currentId:(NSString*)currentId parentId:(nullable NSString*)parentId canSelect:(BOOL)canSelect;
19 |
20 | + (WMZTreeParam*)initWithName:(NSString*)name currentId:(NSString*)currentId parentId:(nullable NSString*)parentId;
21 |
22 | + (WMZTreeParam*)initWithName:(NSString*)name currentId:(NSString*)currentId parentId:(nullable NSString*)parentId canSelect:(BOOL)canSelect;
23 | @end
24 |
25 | NS_ASSUME_NONNULL_END
26 |
--------------------------------------------------------------------------------
/WMZTreeView.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |s|
2 |
3 | s.name = "WMZTreeView"
4 | s.version = "1.1.1"
5 | s.platform = :ios, "8.0"
6 | s.requires_arc = true
7 | s.license = "Copyright (c) 2019年 WMZ. All rights reserved."
8 | s.summary = "类似前端elementUI的树形控件,可自定义节点内容,支持无限极节点,可拖拽增删节点等待,非递归实现
9 | "
10 | s.description = <<-DESC
11 | 可自定义节点内容,支持无限极节点,可拖拽增删节点等待,非递归实现
12 | DESC
13 | s.homepage = "https://github.com/wwmz/WMZTreeView"
14 | s.license = "MIT"
15 | s.author = { "wmz" => "925457662@qq.com" }
16 | s.source = { :git => "https://github.com/wwmz/WMZTreeView.git", :tag => s.version.to_s }
17 | s.source_files = "WMZTreeView/WMZTreeView/**/*.{h,m}"
18 | s.resources = "WMZTreeView/WMZTreeView/WMZTreeView.bundle"
19 | s.framework = 'UIKit'
20 | s.user_target_xcconfig = { 'CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES' => 'YES' }
21 |
22 | end
23 |
--------------------------------------------------------------------------------
/WMZTreeViewTests/WMZTreeViewTests.m:
--------------------------------------------------------------------------------
1 | //
2 | // WMZTreeViewTests.m
3 | // WMZTreeViewTests
4 | //
5 | // Created by wmz on 2019/10/24.
6 | // Copyright © 2019 wmz. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface WMZTreeViewTests : XCTestCase
12 |
13 | @end
14 |
15 | @implementation WMZTreeViewTests
16 |
17 | - (void)setUp {
18 | // Put setup code here. This method is called before the invocation of each test method in the class.
19 | }
20 |
21 | - (void)tearDown {
22 | // Put teardown code here. This method is called after the invocation of each test method in the class.
23 | }
24 |
25 | - (void)testExample {
26 | // This is an example of a functional test case.
27 | // Use XCTAssert and related functions to verify your tests produce the correct results.
28 | }
29 |
30 | - (void)testPerformanceExample {
31 | // This is an example of a performance test case.
32 | [self measureBlock:^{
33 | // Put the code you want to measure the time of here.
34 | }];
35 | }
36 |
37 | @end
38 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 WMZ
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/WMZTreeView/WMZTreeView/WMZTreeProcotol.h:
--------------------------------------------------------------------------------
1 | //
2 | // WMZTreeProcotol.h
3 | // WMZTreeView
4 | //
5 | // Created by wmz on 2022/10/12.
6 | // Copyright © 2022 wmz. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | NS_ASSUME_NONNULL_BEGIN
12 |
13 | @protocol WMZTreeProcotol
14 | /// 能否选中 默认NO
15 | @property (nonatomic, assign) BOOL canSelect;
16 | /// 是否展开 默认NO
17 | @property (nonatomic, assign) BOOL isExpand;
18 | /// name
19 | @property (nonatomic, copy) NSString *name;
20 | /// 其他数据
21 | @property (nonatomic, strong) id data;
22 | /// 当前节点ID 必传
23 | @property (nonatomic, copy) NSString *currentId;
24 | /// 父节点ID
25 | @property (nonatomic, copy, nullable) NSString *parentId;
26 | /// 子数据
27 | @property (nonatomic, strong) NSMutableArray *> *children;
28 | ///custom
29 | /// 半选中
30 | @property (nonatomic, assign) BOOL halfSelect;
31 | /// 全选中
32 | @property (nonatomic, assign) BOOL isSelected;
33 | /// 深度
34 | @property (nonatomic, assign) NSInteger depath;
35 | /// 半选中
36 | @property (nonatomic, assign) BOOL tempHalfSelect;
37 | /// 全选中
38 | @property (nonatomic, assign) BOOL isTempSelected;
39 |
40 | @end
41 |
42 | NS_ASSUME_NONNULL_END
43 |
--------------------------------------------------------------------------------
/WMZTreeView/WMZTreeView/WMZTreeCustomCell.h:
--------------------------------------------------------------------------------
1 | //
2 | // WMZTreeCustomCell.h
3 | // WMZTree
4 | //
5 | // Created by wmz on 2019/10/19.
6 | // Copyright © 2019 wmz. All rights reserved.
7 | //
8 |
9 | #import "WMZTreeProcotol.h"
10 | #import "WMZTreeViewParam.h"
11 | NS_ASSUME_NONNULL_BEGIN
12 | @protocol WMZTreeCustomDelagete
13 |
14 | /// 勾选了节点
15 | /// @param checkStrictly 是否关联父子节点
16 | - (void)selectNode:(NSObject*)param checkStrictly:(BOOL)checkStrictly;
17 |
18 | /// 节点上其他UI的交互
19 | - (void)userWithNode:(NSObject*)param param:(id)data cell:(UITableViewCell*)cell;
20 |
21 | @end
22 | @interface WMZTreeCustomCell : UITableViewCell
23 | @property (nonatomic, strong) UIButton *icon;
24 | @property (nonatomic, strong) UILabel *la;
25 | @property (nonatomic, strong) UIButton *check;
26 | @property (nonatomic, strong) WMZTreeViewParam *parentModel;
27 | @property (nonatomic, strong) NSObject *model;
28 | @property (nonatomic, weak) id delagete;
29 | - (void)UI;
30 | - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier parentModel:(WMZTreeViewParam*)parentModel;
31 | @end
32 |
33 | NS_ASSUME_NONNULL_END
34 |
--------------------------------------------------------------------------------
/WMZTreeView/WMZTreeView/WMZTreeBaseView.h:
--------------------------------------------------------------------------------
1 | //
2 | // WMZTreeBaseView.h
3 | // WMZTree
4 | //
5 | // Created by wmz on 2019/10/19.
6 | // Copyright © 2019 wmz. All rights reserved.
7 | //
8 | #import "WMZTreeViewParam.h"
9 | #import "WMZTreeParam.h"
10 |
11 | NS_ASSUME_NONNULL_BEGIN
12 |
13 | @interface WMZTreeBaseView : UIView
14 |
15 | @property(nonatomic, strong) WMZTreeViewParam *param;
16 | /// 转换为树形
17 | @property(nonatomic, strong) WMZTreeParam *tree;
18 | /// 主视图
19 | @property(nonatomic, strong) UITableView *table;
20 | /// 已展开的树形数组
21 | @property(nonatomic, strong, readonly) NSMutableArray *>*data;
22 | /// 储存的model字典
23 | @property(nonatomic, strong, readonly) NSMutableDictionary *>*tmpDic;
24 | /// 全部字典
25 | @property(nonatomic, strong, readonly) NSMutableDictionary *>*dic;
26 | /// 数据为空的占位显示图
27 | @property(nonatomic, strong, readonly) UIView *emptyView;
28 | /// 字典转模型
29 | - (NSObject*)dictionaryToParam:(NSDictionary*)dic;
30 | /// 寻找所有子节点
31 | - (NSMutableArray*)getSonData:(NSObject*)node type:(TreeDataType)type;
32 | ///寻找所有父节点
33 | - (NSArray*)searchAllParentNode:(NSObject *)param;
34 | ///处理空视图
35 | - (void)setUpEmptyView:(NSDictionary*)dic;
36 |
37 | @end
38 |
39 | NS_ASSUME_NONNULL_END
40 |
--------------------------------------------------------------------------------
/WMZTreeViewUITests/WMZTreeViewUITests.m:
--------------------------------------------------------------------------------
1 | //
2 | // WMZTreeViewUITests.m
3 | // WMZTreeViewUITests
4 | //
5 | // Created by wmz on 2019/10/24.
6 | // Copyright © 2019 wmz. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface WMZTreeViewUITests : XCTestCase
12 |
13 | @end
14 |
15 | @implementation WMZTreeViewUITests
16 |
17 | - (void)setUp {
18 | // Put setup code here. This method is called before the invocation of each test method in the class.
19 |
20 | // In UI tests it is usually best to stop immediately when a failure occurs.
21 | self.continueAfterFailure = NO;
22 |
23 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this.
24 | }
25 |
26 | - (void)tearDown {
27 | // Put teardown code here. This method is called after the invocation of each test method in the class.
28 | }
29 |
30 | - (void)testExample {
31 | // UI tests must launch the application that they test.
32 | XCUIApplication *app = [[XCUIApplication alloc] init];
33 | [app launch];
34 |
35 | // Use recording to get started writing UI tests.
36 | // Use XCTAssert and related functions to verify your tests produce the correct results.
37 | }
38 |
39 | - (void)testLaunchPerformance {
40 | if (@available(macOS 10.15, iOS 13.0, tvOS 13.0, *)) {
41 | // This measures how long it takes to launch your application.
42 | [self measureWithMetrics:@[XCTOSSignpostMetric.applicationLaunchMetric] block:^{
43 | [[[XCUIApplication alloc] init] launch];
44 | }];
45 | }
46 | }
47 |
48 | @end
49 |
--------------------------------------------------------------------------------
/WMZTreeView/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/WMZTreeView/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/WMZTreeView/WMZTreeView/WMZTreeView.h:
--------------------------------------------------------------------------------
1 | //
2 | // WMZTreeView.h
3 | // WMZTree
4 | //
5 | // Created by wmz on 2019/10/17.
6 | // Copyright © 2019 wmz. All rights reserved.
7 | //
8 | #import "WMZTreeBaseView.h"
9 |
10 | NS_ASSUME_NONNULL_BEGIN
11 |
12 | @interface WMZTreeView : WMZTreeBaseView
13 |
14 | /// 初始化
15 | - (instancetype)initWithParam:(WMZTreeViewParam*)param;
16 |
17 | /// 更新或者设置子节点数组
18 | /// @param currrentID 当前节点 (传@""表示更新第一级)
19 | /// @param data 子节点数组
20 | /// @return BOOL 是否更新或者设置成功
21 | - (BOOL)updateKeyChildren:(NSString*)currrentID data:(NSArray*)data;
22 |
23 | /// 获取当前选中的节点数组
24 | /// @param halfSelect 包含半选中
25 | /// @return NSArray 返回数组
26 | - (NSArray*)getCheckedNodesWithHalfSelect:(BOOL)halfSelect;
27 |
28 | /// 为 Tree 中的一个节点追加一个子节点
29 | /// @param currrentID 当前节点
30 | /// @param param 子节点数据
31 | /// @return BOOL 是否追加成功
32 | - (BOOL)append:(NSString*)currrentID node:(NSObject*)param;
33 |
34 | /// 删除节点
35 | /// @param currrentID 当前节点
36 | /// @return BOOL 是否追加成功
37 | - (BOOL)remove:(NSString*)currrentID;
38 |
39 | /// 为 Tree 的一个节点的前面增加一个节点
40 | /// @param currrentID 当前节点
41 | /// @param param 子节点数据
42 | /// @return BOOL 是否追加成功
43 | - (BOOL)insertBefore:(NSString*)currrentID node:(NSObject*)param;
44 |
45 | /// 为 Tree 的一个节点的后面增加一个节点
46 | /// @param currrentID 当前节点
47 | /// @param param 子节点数据
48 | /// @return BOOL 是否追加成功
49 | - (BOOL)insertAfter:(NSString*)currrentID node:(NSObject*)param;
50 |
51 | /// 更新编辑状态
52 | - (void)updateEditing;
53 |
54 | /// 传进来的是JSON数组 转为树形模型
55 | - (void)changeJSONtToTreeModel:(NSArray*)data type:(TreeDataType)type;
56 |
57 | /// 全选
58 | - (void)selectAll;
59 |
60 | /// 全部取消选中
61 | - (void)notSelectAll;
62 |
63 | /// 获取父节点
64 | - (NSObject*)getParentId:(NSString*)currrentID;
65 |
66 | /// 更新数据
67 | - (void)update;
68 |
69 | @end
70 |
71 | NS_ASSUME_NONNULL_END
72 |
--------------------------------------------------------------------------------
/WMZTreeView/AppDelegate.m:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.m
3 | // WMZTree
4 | //
5 | // Created by wmz on 2019/10/17.
6 | // Copyright © 2019 wmz. All rights reserved.
7 | //
8 |
9 | #import "AppDelegate.h"
10 | #import "DemoVC.h"
11 | #import "ViewController.h"
12 | @interface AppDelegate ()
13 |
14 | @end
15 |
16 | @implementation AppDelegate
17 | @synthesize window = _window;
18 |
19 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
20 | // Override point for customization after application launch.
21 | if (@available(ios 13, *)) {
22 |
23 | } else {
24 | UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:[[DemoVC alloc]init]];
25 | self.window.rootViewController = nav;
26 | [self.window makeKeyAndVisible];
27 | }
28 | return YES;
29 | }
30 |
31 |
32 | #pragma mark - UISceneSession lifecycle
33 |
34 |
35 | - (UISceneConfiguration *)application:(UIApplication *)application configurationForConnectingSceneSession:(UISceneSession *)connectingSceneSession options:(UISceneConnectionOptions *)options {
36 | // Called when a new scene session is being created.
37 | // Use this method to select a configuration to create the new scene with.
38 | return [[UISceneConfiguration alloc] initWithName:@"Default Configuration" sessionRole:connectingSceneSession.role];
39 | }
40 |
41 |
42 | - (void)application:(UIApplication *)application didDiscardSceneSessions:(NSSet *)sceneSessions {
43 | // Called when the user discards a scene session.
44 | // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
45 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return.
46 | }
47 |
48 |
49 | @end
50 |
--------------------------------------------------------------------------------
/WMZTreeView/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "20x20",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "20x20",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "29x29",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "29x29",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "40x40",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "40x40",
31 | "scale" : "3x"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "size" : "60x60",
36 | "scale" : "2x"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "size" : "60x60",
41 | "scale" : "3x"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "size" : "20x20",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "size" : "20x20",
51 | "scale" : "2x"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "size" : "29x29",
56 | "scale" : "1x"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "size" : "29x29",
61 | "scale" : "2x"
62 | },
63 | {
64 | "idiom" : "ipad",
65 | "size" : "40x40",
66 | "scale" : "1x"
67 | },
68 | {
69 | "idiom" : "ipad",
70 | "size" : "40x40",
71 | "scale" : "2x"
72 | },
73 | {
74 | "idiom" : "ipad",
75 | "size" : "76x76",
76 | "scale" : "1x"
77 | },
78 | {
79 | "idiom" : "ipad",
80 | "size" : "76x76",
81 | "scale" : "2x"
82 | },
83 | {
84 | "idiom" : "ipad",
85 | "size" : "83.5x83.5",
86 | "scale" : "2x"
87 | },
88 | {
89 | "idiom" : "ios-marketing",
90 | "size" : "1024x1024",
91 | "scale" : "1x"
92 | }
93 | ],
94 | "info" : {
95 | "version" : 1,
96 | "author" : "xcode"
97 | }
98 | }
--------------------------------------------------------------------------------
/WMZTreeView/SceneDelegate.m:
--------------------------------------------------------------------------------
1 | #import "SceneDelegate.h"
2 | #import "DemoVC.h"
3 | #import "ViewController.h"
4 | @interface SceneDelegate ()
5 |
6 | @end
7 |
8 | @implementation SceneDelegate
9 |
10 |
11 | - (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions {
12 | UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:[[DemoVC alloc]init]];
13 | self.window.rootViewController = nav;
14 |
15 | [self.window makeKeyAndVisible];
16 | }
17 |
18 |
19 | - (void)sceneDidDisconnect:(UIScene *)scene {
20 | // Called as the scene is being released by the system.
21 | // This occurs shortly after the scene enters the background, or when its session is discarded.
22 | // Release any resources associated with this scene that can be re-created the next time the scene connects.
23 | // The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead).
24 | }
25 |
26 |
27 | - (void)sceneDidBecomeActive:(UIScene *)scene {
28 | // Called when the scene has moved from an inactive state to an active state.
29 | // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
30 | }
31 |
32 |
33 | - (void)sceneWillResignActive:(UIScene *)scene {
34 | // Called when the scene will move from an active state to an inactive state.
35 | // This may occur due to temporary interruptions (ex. an incoming phone call).
36 | }
37 |
38 |
39 | - (void)sceneWillEnterForeground:(UIScene *)scene {
40 | // Called as the scene transitions from the background to the foreground.
41 | // Use this method to undo the changes made on entering the background.
42 | }
43 |
44 |
45 | - (void)sceneDidEnterBackground:(UIScene *)scene {
46 | // Called as the scene transitions from the foreground to the background.
47 | // Use this method to save data, release shared resources, and store enough scene-specific state information
48 | // to restore the scene back to its current state.
49 | }
50 |
51 |
52 | @end
53 |
--------------------------------------------------------------------------------
/WMZTreeView/WMZTreeView/WMZTreeParam.m:
--------------------------------------------------------------------------------
1 | //
2 | // WMZTreeParam.m
3 | // WMZTree
4 | //
5 | // Created by wmz on 2019/10/17.
6 | // Copyright © 2019 wmz. All rights reserved.
7 | //
8 |
9 | #import "WMZTreeParam.h"
10 |
11 | @implementation WMZTreeParam
12 | @synthesize canSelect = _canSelect;
13 | @synthesize isExpand = _isExpand;
14 | @synthesize name = _name;
15 | @synthesize data = _data;
16 | @synthesize currentId = _currentId;
17 | @synthesize parentId = _parentId;
18 | @synthesize children = _children;
19 | @synthesize depath = _depath;
20 | @synthesize halfSelect = _halfSelect;
21 | @synthesize isSelected = _isSelected;
22 | @synthesize isTempSelected = _isTempSelected;
23 | @synthesize tempHalfSelect = _tempHalfSelect;
24 |
25 | - (instancetype)initWithName:(NSString*)name currentId:(NSString*)currentId parentId:(nullable NSString*)parentId{
26 | return [self initWithName:name currentId:currentId parentId:parentId canSelect:YES];
27 | }
28 |
29 | - (instancetype)initWithName:(NSString*)name currentId:(NSString*)currentId parentId:(nullable NSString*)parentId canSelect:(BOOL)canSelect{
30 | if(self = [super init]){
31 | self.name = name;
32 | self.currentId = currentId;
33 | self.parentId = parentId;
34 | self.canSelect = parentId;
35 | }
36 | return self;
37 | }
38 |
39 | + (WMZTreeParam*)initWithName:(NSString*)name currentId:(NSString*)currentId parentId:(nullable NSString*)parentId{
40 | return [[self alloc] initWithName:name currentId:currentId parentId:parentId];
41 | }
42 |
43 | + (WMZTreeParam *)initWithName:(NSString *)name currentId:(NSString *)currentId parentId:(NSString *)parentId canSelect:(BOOL)canSelect{
44 | return [[self alloc] initWithName:name currentId:currentId parentId:parentId canSelect:canSelect];
45 | }
46 |
47 | - (instancetype)init{
48 | if (self = [super init]) {
49 | _canSelect = YES;
50 | }
51 | return self;
52 | }
53 |
54 | WMZTreeParam * TreeParam(void){
55 | return [WMZTreeParam new];
56 | }
57 |
58 | - (NSMutableArray *> *)children{
59 | if (!_children) {
60 | _children = [NSMutableArray new];
61 | }
62 | return _children;
63 | }
64 |
65 | @end
66 |
--------------------------------------------------------------------------------
/WMZTreeView/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 | LSRequiresIPhoneOS
22 |
23 | UIApplicationSceneManifest
24 |
25 | UIApplicationSupportsMultipleScenes
26 |
27 | UISceneConfigurations
28 |
29 | UIWindowSceneSessionRoleApplication
30 |
31 |
32 | UISceneConfigurationName
33 | Default Configuration
34 | UISceneDelegateClassName
35 | SceneDelegate
36 | UISceneStoryboardFile
37 | Main
38 |
39 |
40 |
41 |
42 | UILaunchStoryboardName
43 | LaunchScreen
44 | UIMainStoryboardFile
45 | Main
46 | UIRequiredDeviceCapabilities
47 |
48 | armv7
49 |
50 | UISupportedInterfaceOrientations
51 |
52 | UIInterfaceOrientationPortrait
53 | UIInterfaceOrientationLandscapeLeft
54 | UIInterfaceOrientationLandscapeRight
55 |
56 | UISupportedInterfaceOrientations~ipad
57 |
58 | UIInterfaceOrientationPortrait
59 | UIInterfaceOrientationPortraitUpsideDown
60 | UIInterfaceOrientationLandscapeLeft
61 | UIInterfaceOrientationLandscapeRight
62 |
63 |
64 |
65 |
--------------------------------------------------------------------------------
/WMZTreeView/DemoVC.m:
--------------------------------------------------------------------------------
1 |
2 | //
3 | // DemoVC.m
4 | // WMZTree
5 | //
6 | // Created by wmz on 2019/10/23.
7 | // Copyright © 2019 wmz. All rights reserved.
8 | //
9 |
10 | #import "DemoVC.h"
11 | #import "ViewController.h"
12 | #import "WMZTreeConfig.h"
13 | @interface DemoVC ()
14 | @property(nonatomic,strong)UITableView *ta;
15 | @property(nonatomic,strong)NSArray *taData;
16 | @end
17 |
18 | @implementation DemoVC
19 |
20 | - (void)viewDidLoad {
21 | [super viewDidLoad];
22 |
23 | self.view.backgroundColor = [UIColor whiteColor];
24 |
25 | UITableView *ta = [[UITableView alloc]initWithFrame:CGRectMake(0, 88, self.view.frame.size.width,self.view.frame.size.height-88) style:UITableViewStyleGrouped];
26 | [self.view addSubview:ta];
27 | if (@available(iOS 11.0, *)) {
28 | ta.estimatedRowHeight = 0.01;
29 | ta.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
30 | }
31 | ta.estimatedRowHeight = 0.01;
32 | ta.dataSource = self;
33 | ta.delegate = self;
34 | self.ta = ta;
35 | }
36 |
37 |
38 | - (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section{
39 | return 0.01;
40 | }
41 |
42 | - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section{
43 | return 0.01;
44 | }
45 |
46 | - (UIView*)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section{
47 | return nil;
48 | }
49 |
50 | - (UIView*)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section{
51 | return nil;
52 | }
53 |
54 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
55 | return [self.taData count];
56 | }
57 |
58 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
59 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"];
60 | if (!cell) {
61 | cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"cell"];
62 | }
63 | cell.textLabel.font = [UIFont systemFontOfSize:14.0f];
64 | cell.textLabel.text = self.taData[indexPath.row];
65 | return cell;
66 | }
67 |
68 |
69 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
70 | ViewController *vc = [ViewController new];
71 | vc.type = indexPath.row+1;
72 | [self.navigationController pushViewController:vc animated:YES];
73 | }
74 |
75 | - (NSArray *)taData{
76 | if (!_taData) {
77 | _taData = @[@"正常多叉树显示",@"可选中树形+选中高亮显示",@"自定义节点内容+增删节点",@"手风琴效果+指定层级可勾选 ",@"勾选不关联父节点和子节点+默认选中+默认全部展开",@"开启拖拽 ",@"传字典数据",@"传实现协议的数据"];
78 | }
79 | return _taData;
80 | }
81 |
82 | @end
83 |
84 |
--------------------------------------------------------------------------------
/WMZTreeView/WMZMyCell.m:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | //
8 | // WMZMyCell.m
9 | // WMZTree
10 | //
11 | // Created by wmz on 2019/10/22.
12 | // Copyright © 2019 wmz. All rights reserved.
13 | //
14 |
15 | #import "WMZMyCell.h"
16 | @interface WMZMyCell()
17 | @property(nonatomic,strong)UIButton *add;
18 | @property(nonatomic,strong)UIButton *delete;
19 | @end
20 | @implementation WMZMyCell
21 |
22 | - (void)layoutSubviews{
23 | [super layoutSubviews];
24 | //布局
25 | CGFloat offset = 10;
26 | CGFloat height = self.contentView.frame.size.height;
27 | self.la.frame = CGRectMake(CGRectGetMaxX(self.icon.frame),offset , 80, height-offset*2);
28 | self.add.frame = CGRectMake(CGRectGetMaxX(self.la.frame)+offset, offset, 60, height-offset*2);
29 | self.delete.frame = CGRectMake(CGRectGetMaxX(self.add.frame)+offset, offset, 60, height-offset*2);
30 | }
31 |
32 | - (void)UI{
33 | //继承父类的UI
34 | [super UI];
35 | [self.contentView addSubview:self.add];
36 | [self.contentView addSubview:self.delete];
37 | [self.add setTitle:@"append" forState:UIControlStateNormal];
38 | [self.delete setTitle:@"delete" forState:UIControlStateNormal];
39 | self.add.titleLabel.font = [UIFont systemFontOfSize:14.0];
40 | self.delete.titleLabel.font = [UIFont systemFontOfSize:14.0];
41 | [self.add setTitleColor:[UIColor orangeColor] forState:UIControlStateNormal];
42 | [self.delete setTitleColor:[UIColor orangeColor] forState:UIControlStateNormal];
43 | [self.add addTarget:self action:@selector(addAction) forControlEvents:UIControlEventTouchUpInside];
44 | [self.delete addTarget:self action:@selector(deleteAction) forControlEvents:UIControlEventTouchUpInside];
45 | }
46 |
47 | - (void)addAction{
48 | //回调代理
49 | if (self.delagete&&[self.delagete respondsToSelector:@selector(userWithNode:param:cell:)]) {
50 | [self.delagete userWithNode:self.model param:@"add" cell:self];
51 | }
52 | }
53 |
54 | - (void)deleteAction{
55 | if (self.delagete&&[self.delagete respondsToSelector:@selector(userWithNode:param:cell:)]) {
56 | [self.delagete userWithNode:self.model param:@"delete" cell:self];
57 | }
58 | }
59 |
60 | - (UIButton *)add{
61 | if (!_add) {
62 | _add = [UIButton buttonWithType:UIButtonTypeCustom];
63 | }
64 | return _add;
65 | }
66 |
67 | - (UIButton *)delete{
68 | if (!_delete) {
69 | _delete = [UIButton buttonWithType:UIButtonTypeCustom];
70 | }
71 | return _delete;
72 | }
73 |
74 | - (void)awakeFromNib {
75 | [super awakeFromNib];
76 | // Initialization code
77 | }
78 |
79 | - (void)setSelected:(BOOL)selected animated:(BOOL)animated {
80 | [super setSelected:selected animated:animated];
81 |
82 | // Configure the view for the selected state
83 | }
84 |
85 | @end
86 |
--------------------------------------------------------------------------------
/WMZTreeView/WMZTreeView/WMZTreeConfig.h:
--------------------------------------------------------------------------------
1 | //
2 | // WMZTreeConfig.h
3 | // WMZTree
4 | //
5 | // Created by wmz on 2019/10/17.
6 | // Copyright © 2019 wmz. All rights reserved.
7 | //
8 |
9 | #ifndef WMZTreeConfig_h
10 | #define WMZTreeConfig_h
11 |
12 | #import
13 |
14 | #define TreeColor(rgbValue) [UIColor colorWithRed:((float)((rgbValue & 0xFF0000) >> 16))/255.0 green:((float)((rgbValue & 0xFF00) >> 8))/255.0 blue:((float)(rgbValue & 0xFF))/255.0 alpha:1.0]
15 |
16 | #define TreeWidth [UIScreen mainScreen].bounds.size.width
17 | #define TreeHeight [UIScreen mainScreen].bounds.size.height
18 |
19 | #define TreeAlert(TITLE,MESSAGE,QUVC) UIAlertController *alertController = [UIAlertController alertControllerWithTitle:TITLE message:MESSAGE preferredStyle:UIAlertControllerStyleAlert];\
20 | [alertController addAction:[UIAlertAction actionWithTitle:@"确定"style:UIAlertActionStyleDefault handler:nil]];\
21 | [QUVC presentViewController:alertController animated:YES completion:nil];
22 |
23 |
24 | #define WMZTreePropStatementAndPropSetFuncStatement(propertyModifier,className, propertyPointerType, propertyName) \
25 | @property(nonatomic,propertyModifier)propertyPointerType propertyName; \
26 | - (className * (^) (propertyPointerType propertyName)) propertyName##Set;
27 |
28 | #define WMZTreePropSetFuncImplementation(className, propertyPointerType, propertyName) \
29 | - (className * (^) (propertyPointerType propertyName))propertyName##Set{ \
30 | return ^(propertyPointerType propertyName) { \
31 | self->_##propertyName = propertyName; \
32 | return self; \
33 | }; \
34 | }
35 |
36 | typedef enum :NSInteger{
37 | ///获取全部包括自身
38 | TreeDataAllWithSelf,
39 | ///获取全部数据不包括自身
40 | TreeDataGetSelectAll,
41 | ///获取全部(以下全不包括自身)
42 | TreeDataAll,
43 | ///全部选中的子节点
44 | TreeDataSelectAll,
45 | ///获取同级的节点
46 | TreeDataSameLevel,
47 | ///获取展开的和第一级
48 | TreeDataExpandOrNotParent,
49 | ///增加
50 | TreeDataInsert,
51 | ///删除
52 | TreeDataDelete,
53 | }TreeDataType;
54 |
55 | /// 点击
56 | typedef void (^NodeClickBlock)(id node);
57 |
58 | /// 勾选
59 | typedef void (^NodeCheckChange)(id node,BOOL isSelect);
60 |
61 | /// cell
62 | typedef UITableViewCell* (^CustomTreeCell)(id model,NSIndexPath* path,UITableView *table,id param);
63 |
64 | /// cell交互
65 | typedef void (^CellUserEnabled)(id model,NSIndexPath* path,UITableView *table,id userInfo);
66 |
67 | /// cell拖拽
68 | typedef void (^CellDraggable)(NSIndexPath* sourceIndexPath,NSIndexPath* destinationIndexPath,UITableView *table);
69 |
70 | /// cell height
71 | typedef CGFloat (^CustomTreeCellHeight)(id model,NSIndexPath* path,UITableView *table);
72 |
73 | #endif /* WMZTreeConfig_h */
74 |
--------------------------------------------------------------------------------
/WMZTreeView/WMZTreeView/WMZTreeViewParam.m:
--------------------------------------------------------------------------------
1 |
2 |
3 | //
4 | // WMZTreeViewParam.m
5 | // WMZTree
6 | //
7 | // Created by wmz on 2019/10/19.
8 | // Copyright © 2019 wmz. All rights reserved.
9 | //
10 |
11 | #import "WMZTreeViewParam.h"
12 |
13 | WMZTreeParamKey const WMZTreeName = @"name";
14 | WMZTreeParamKey const WMZTreeImage = @"image";
15 | WMZTreeParamKey const WMZTreeCurrentId = @"currentId";
16 | WMZTreeParamKey const WMZTreeParentId = @"parentId";
17 | WMZTreeParamKey const WMZTreeExpand = @"isExpand";
18 | WMZTreeParamKey const WMZTreeCanSelect = @"canSelect";
19 | WMZTreeParamKey const WMZTreeExistData = @"data";
20 | WMZTreeParamKey const WMZTreeChildren = @"children";
21 |
22 | @implementation WMZTreeViewParam
23 | WMZTreePropSetFuncImplementation(WMZTreeViewParam, NSArray*, wData)
24 | WMZTreePropSetFuncImplementation(WMZTreeViewParam, CGRect, wFrame)
25 | WMZTreePropSetFuncImplementation(WMZTreeViewParam, NSDictionary*, wEmptyData)
26 | WMZTreePropSetFuncImplementation(WMZTreeViewParam, UIColor*, wHighlightCurrent)
27 | WMZTreePropSetFuncImplementation(WMZTreeViewParam, CGFloat, wNodeTextFont)
28 | WMZTreePropSetFuncImplementation(WMZTreeViewParam, UIColor*, wNodeTextColor)
29 | WMZTreePropSetFuncImplementation(WMZTreeViewParam, NSArray*, wDefaultExpandedKeys)
30 | WMZTreePropSetFuncImplementation(WMZTreeViewParam, BOOL, wCanMultipleSelect)
31 | WMZTreePropSetFuncImplementation(WMZTreeViewParam, BOOL, wShowOnly)
32 | WMZTreePropSetFuncImplementation(WMZTreeViewParam, BOOL, wShowCheckbox)
33 | WMZTreePropSetFuncImplementation(WMZTreeViewParam, BOOL, wAccordion)
34 | WMZTreePropSetFuncImplementation(WMZTreeViewParam, BOOL, wCheckStrictly)
35 | WMZTreePropSetFuncImplementation(WMZTreeViewParam, BOOL, wDraggable)
36 | WMZTreePropSetFuncImplementation(WMZTreeViewParam, BOOL, wDefaultExpandAll)
37 | WMZTreePropSetFuncImplementation(WMZTreeViewParam, BOOL, wHideExpanIcon)
38 | WMZTreePropSetFuncImplementation(WMZTreeViewParam, NSInteger, wIndent)
39 | WMZTreePropSetFuncImplementation(WMZTreeViewParam, NSString*, wExpandIcon)
40 | WMZTreePropSetFuncImplementation(WMZTreeViewParam, NSString*, wSelectExpandIcon)
41 | WMZTreePropSetFuncImplementation(WMZTreeViewParam, NSString*, wCheckIcon)
42 | WMZTreePropSetFuncImplementation(WMZTreeViewParam, NSString*, wSelectCheckIcon)
43 | WMZTreePropSetFuncImplementation(WMZTreeViewParam, NSString*, wHalfSelectCheckIcon)
44 | WMZTreePropSetFuncImplementation(WMZTreeViewParam, NodeClickBlock, wEventNodeClick)
45 | WMZTreePropSetFuncImplementation(WMZTreeViewParam, NodeCheckChange, wEventCheckChange)
46 | WMZTreePropSetFuncImplementation(WMZTreeViewParam, CustomTreeCell, wEventTreeCell)
47 | WMZTreePropSetFuncImplementation(WMZTreeViewParam, CellUserEnabled, wEventCellUserEnabled)
48 | WMZTreePropSetFuncImplementation(WMZTreeViewParam, CustomTreeCellHeight,wEventCellHeight)
49 | WMZTreePropSetFuncImplementation(WMZTreeViewParam, CellDraggable, wEventNodeDraggable)
50 | WMZTreeViewParam * TreeViewParam(void){
51 | return [WMZTreeViewParam new];
52 | }
53 |
54 | - (instancetype)init{
55 | if (self = [super init]) {
56 | _wNodeTextFont = 15.0f;
57 | _wNodeTextColor = [UIColor blackColor];
58 | _wIndent = 2;
59 | _wExpandIcon = @"close";
60 | _wSelectExpandIcon = @"open";
61 | _wCheckIcon = @"treeCheck";
62 | _wSelectCheckIcon = @"treeCheckSelect";
63 | _wHalfSelectCheckIcon = @"treeHalfSelect";
64 | _wCheckStrictly = NO;
65 | _wCanMultipleSelect = YES;
66 | }
67 | return self;
68 | }
69 |
70 | @end
71 |
--------------------------------------------------------------------------------
/WMZTreeView/WMZTreeView/WMZTreeViewParam.h:
--------------------------------------------------------------------------------
1 | //
2 | // WMZTreeViewParam.h
3 | // WMZTree
4 | //
5 | // Created by wmz on 2019/10/19.
6 | // Copyright © 2019 wmz. All rights reserved.
7 | //
8 |
9 | #import "WMZTreeConfig.h"
10 | NS_ASSUME_NONNULL_BEGIN
11 |
12 |
13 | typedef NSString *WMZTreeParamKey NS_STRING_ENUM;
14 | // name
15 | FOUNDATION_EXPORT WMZTreeParamKey const WMZTreeName;
16 | ///image
17 | FOUNDATION_EXPORT WMZTreeParamKey const WMZTreeImage;
18 | // currentId
19 | FOUNDATION_EXPORT WMZTreeParamKey const WMZTreeCurrentId;
20 | // parentId
21 | FOUNDATION_EXPORT WMZTreeParamKey const WMZTreeParentId;
22 | // isExpand
23 | FOUNDATION_EXPORT WMZTreeParamKey const WMZTreeExpand;
24 | // canSelect
25 | FOUNDATION_EXPORT WMZTreeParamKey const WMZTreeCanSelect;
26 | // data
27 | FOUNDATION_EXPORT WMZTreeParamKey const WMZTreeExistData;
28 | // children
29 | FOUNDATION_EXPORT WMZTreeParamKey const WMZTreeChildren;
30 |
31 | @interface WMZTreeViewParam : NSObject
32 | WMZTreeViewParam * TreeViewParam(void);
33 |
34 | /// 展示数据 必传
35 | WMZTreePropStatementAndPropSetFuncStatement(strong, WMZTreeViewParam, NSArray*, wData)
36 | /// frame 必传
37 | WMZTreePropStatementAndPropSetFuncStatement(assign, WMZTreeViewParam, CGRect, wFrame)
38 |
39 | /// 内容为空的时候展示的数据 default nil (image为图片 name为文字)
40 | WMZTreePropStatementAndPropSetFuncStatement(copy, WMZTreeViewParam, NSDictionary*, wEmptyData)
41 | /// 高亮当前选中节点颜色,default是 nil。
42 | WMZTreePropStatementAndPropSetFuncStatement(strong, WMZTreeViewParam, UIColor*, wHighlightCurrent)
43 | /// 节点的字体大小 default 15.0f;
44 | WMZTreePropStatementAndPropSetFuncStatement(assign, WMZTreeViewParam, CGFloat, wNodeTextFont)
45 | /// 节点的字体颜色 default 333333
46 | WMZTreePropStatementAndPropSetFuncStatement(strong, WMZTreeViewParam, UIColor*, wNodeTextColor)
47 |
48 | /// 是否默认展开所有节点 default NO
49 | WMZTreePropStatementAndPropSetFuncStatement(assign, WMZTreeViewParam, BOOL, wDefaultExpandAll)
50 | /// 同级的能否多选 default YES 设置为NO的时候 wCheckStrictly为NO
51 | WMZTreePropStatementAndPropSetFuncStatement(assign, WMZTreeViewParam, BOOL, wCanMultipleSelect)
52 | /// 在显示复选框的情况下,是否严格的遵循父子互相关联的做法,defualt为 YES
53 | WMZTreePropStatementAndPropSetFuncStatement(assign, WMZTreeViewParam, BOOL, wCheckStrictly)
54 | /// 隐藏展开图标,defualt为 NO
55 | WMZTreePropStatementAndPropSetFuncStatement(assign, WMZTreeViewParam, BOOL, wHideExpanIcon)
56 | /// 节点是否可被选择 default NO
57 | WMZTreePropStatementAndPropSetFuncStatement(assign, WMZTreeViewParam, BOOL, wShowCheckbox)
58 | /// 是否每次只打开一个同级树节点展开 手风琴效果 default NO
59 | WMZTreePropStatementAndPropSetFuncStatement(assign, WMZTreeViewParam, BOOL, wAccordion)
60 | /// 是否开启拖拽节点功能 ⚠️目前只支持没有子节点的节点拖拽 如果有什么方便的办法可以支持多个cell一起拖拽的告知下
61 | WMZTreePropStatementAndPropSetFuncStatement(assign, WMZTreeViewParam, BOOL, wDraggable)
62 |
63 | /// 当canSelect为NO的时候 是否显示(不可点击) defaultNO
64 | WMZTreePropStatementAndPropSetFuncStatement(assign, WMZTreeViewParam, BOOL, wShowOnly)
65 | /// 默认展开的节点的 key 的数组 default nil
66 | WMZTreePropStatementAndPropSetFuncStatement(strong, WMZTreeViewParam, NSArray*, wDefaultExpandedKeys)
67 | /// 相邻级节点间的水平缩进距离 默认2
68 | WMZTreePropStatementAndPropSetFuncStatement(assign, WMZTreeViewParam, NSInteger, wIndent)
69 | /// 自定义树节点的图标
70 | WMZTreePropStatementAndPropSetFuncStatement(strong, WMZTreeViewParam, NSString*, wExpandIcon)
71 | /// 自定义树节点展开的图标
72 | WMZTreePropStatementAndPropSetFuncStatement(strong, WMZTreeViewParam, NSString*, wSelectExpandIcon)
73 | /// 自定义树节点未勾选的图标
74 | WMZTreePropStatementAndPropSetFuncStatement(strong, WMZTreeViewParam, NSString*, wCheckIcon)
75 | /// 自定义树节点勾选的图标
76 | WMZTreePropStatementAndPropSetFuncStatement(strong, WMZTreeViewParam, NSString*, wSelectCheckIcon)
77 | /// 自定义树节点半选中的图标 (没有全选)
78 | WMZTreePropStatementAndPropSetFuncStatement(strong, WMZTreeViewParam, NSString*, wHalfSelectCheckIcon)
79 |
80 |
81 | /// 自定义节点cell
82 | WMZTreePropStatementAndPropSetFuncStatement(copy, WMZTreeViewParam, CustomTreeCell, wEventTreeCell)
83 | /// 自定义cell其他交互
84 | WMZTreePropStatementAndPropSetFuncStatement(copy, WMZTreeViewParam, CellUserEnabled, wEventCellUserEnabled)
85 | /// 自定义cell高度
86 | WMZTreePropStatementAndPropSetFuncStatement(copy, WMZTreeViewParam, CustomTreeCellHeight,wEventCellHeight)
87 | /// 节点被点击时的回调
88 | WMZTreePropStatementAndPropSetFuncStatement(copy, WMZTreeViewParam, NodeClickBlock, wEventNodeClick)
89 | /// 节点拖拽完成回调
90 | WMZTreePropStatementAndPropSetFuncStatement(copy, WMZTreeViewParam, CellDraggable, wEventNodeDraggable)
91 | /// 节点选中状态发生变化时的回调
92 | WMZTreePropStatementAndPropSetFuncStatement(copy, WMZTreeViewParam, NodeCheckChange, wEventCheckChange)
93 |
94 |
95 | @end
96 |
97 | NS_ASSUME_NONNULL_END
98 |
--------------------------------------------------------------------------------
/WMZTreeView/WMZTreeView/WMZTreeCustomCell.m:
--------------------------------------------------------------------------------
1 |
2 | //
3 | // WMZTreeCustomCell.m
4 | // WMZTree
5 | //
6 | // Created by wmz on 2019/10/19.
7 | // Copyright © 2019 wmz. All rights reserved.
8 | //
9 |
10 | #import "WMZTreeCustomCell.h"
11 | #define treeIconWidth 18
12 | @implementation WMZTreeCustomCell
13 |
14 | - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier parentModel:(WMZTreeViewParam*)parentModel{
15 | if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
16 | self.selectionStyle = UITableViewCellSelectionStyleNone;
17 | self.parentModel = parentModel;
18 | [self UI];
19 | }
20 | return self;
21 | }
22 |
23 | - (void)layoutSubviews{
24 | [super layoutSubviews];
25 | CGFloat offset = 10;
26 | CGFloat width = self.contentView.frame.size.width;
27 | CGFloat height = self.contentView.frame.size.height;
28 |
29 | CGRect rect = CGRectMake(self.model.depath<=1?offset:(offset*self.parentModel.wIndent*(self.model.depath-1)), (height-18)/2, [self.icon isHidden]?treeIconWidth:treeIconWidth, treeIconWidth);
30 | if (self.parentModel.wDraggable) {
31 | rect.origin.x-=38;
32 | self.icon.frame = rect;
33 | }else{
34 | self.icon.frame = rect;
35 | }
36 | self.la.frame = CGRectMake(CGRectGetMaxX(self.icon.frame) + 8,offset , width-CGRectGetMaxX(self.icon.frame)-offset*2, height-offset*2);
37 | CGFloat iconHeight = 17;
38 | self.check.frame = CGRectMake(width-iconHeight-offset, (height-iconHeight)/2, iconHeight, iconHeight);
39 | }
40 |
41 | - (void)UI{
42 |
43 | self.icon.userInteractionEnabled = NO;
44 | [self.icon setImage:[self bundleImage:self.parentModel.wExpandIcon] forState:UIControlStateNormal];
45 | [self.icon setImage:[self bundleImage:self.parentModel.wSelectExpandIcon] forState:UIControlStateSelected];
46 | [self.contentView addSubview:self.icon];
47 |
48 | [self.check setImage:[self bundleImage:self.parentModel.wCheckIcon] forState:UIControlStateNormal];
49 | if (!self.parentModel.wCheckStrictly) {
50 | [self.check setImage:[self bundleImage:self.parentModel.wSelectCheckIcon] forState:UIControlStateSelected];
51 | }
52 | [self.check addTarget:self action:@selector(checkAction:) forControlEvents:UIControlEventTouchUpInside];
53 | [self.contentView addSubview:self.check];
54 |
55 | self.la.textColor = self.parentModel.wNodeTextColor;
56 | self.la.font = [UIFont systemFontOfSize:self.parentModel.wNodeTextFont];
57 | [self.contentView addSubview:self.la];
58 | }
59 |
60 | - (void)setModel:(NSObject *)model{
61 | _model = model;
62 | self.la.text = model.name;
63 | self.check.selected = model.isSelected;
64 | self.icon.hidden = !model.children.count ? YES:(self.parentModel.wHideExpanIcon);
65 | if (self.parentModel.wHighlightCurrent&&model.canSelect&&self.parentModel.wShowCheckbox) {
66 | if (self.model.isSelected||
67 | self.model.halfSelect) {
68 | self.la.textColor = self.parentModel.wHighlightCurrent;
69 | }else{
70 | self.la.textColor = self.parentModel.wNodeTextColor;
71 | }
72 | }
73 | if (self.parentModel.wShowCheckbox) {
74 | if (self.parentModel.wShowOnly) {
75 | self.check.hidden = NO;
76 | }else{
77 | self.check.hidden = !model.canSelect;
78 | }
79 | }else{
80 | self.check.hidden = YES;
81 | }
82 | if (!self.icon.hidden) {
83 | self.icon.selected = model.isExpand;
84 | }
85 | if (self.model.halfSelect) {
86 | [self.check setImage:[self bundleImage:self.parentModel.wHalfSelectCheckIcon] forState:UIControlStateNormal];
87 | [self.check setImage:[self bundleImage:self.parentModel.wSelectCheckIcon] forState:UIControlStateSelected];
88 | }else{
89 | [self.check setImage:[self bundleImage:self.parentModel.wCheckIcon] forState:UIControlStateNormal];
90 | [self.check setImage:[self bundleImage:self.parentModel.wSelectCheckIcon] forState:UIControlStateSelected];
91 | }
92 | }
93 |
94 | - (void)checkAction:(UIButton*)btn{
95 | if (!self.model.canSelect) return;
96 | self.model.isSelected = !self.model.isSelected;
97 | if (self.delagete&&[self.delagete respondsToSelector:@selector(selectNode:checkStrictly:)]) {
98 | [self.delagete selectNode:self.model checkStrictly:self.parentModel.wCheckStrictly];
99 | }
100 |
101 | if (self.model.isSelected) {
102 | self.model.halfSelect = NO;
103 | }
104 |
105 | if (self.model.halfSelect) {
106 | [self.check setImage:[self bundleImage:self.parentModel.wHalfSelectCheckIcon] forState:UIControlStateNormal];
107 | [self.check setImage:[self bundleImage:self.parentModel.wSelectCheckIcon] forState:UIControlStateSelected];
108 | }else{
109 | [self.check setImage:[self bundleImage:self.parentModel.wCheckIcon] forState:UIControlStateNormal];
110 | [self.check setImage:[self bundleImage:self.parentModel.wSelectCheckIcon] forState:UIControlStateSelected];
111 | }
112 | btn.selected = ![btn isSelected];
113 | }
114 |
115 | - (UIImage*)bundleImage:(NSString*)name{
116 | NSBundle *bundle = [NSBundle bundleWithPath:[[NSBundle bundleForClass:[WMZTreeCustomCell class]] pathForResource:@"WMZTreeView" ofType:@"bundle"]];
117 | NSString *path = [bundle pathForResource:name ofType:@"png"];
118 | if (!path) {
119 | return [UIImage imageNamed:name];
120 | }else{
121 | return [UIImage imageWithContentsOfFile:path];
122 | }
123 | }
124 |
125 | - (UIButton *)icon{
126 | if (!_icon) {
127 | _icon = [UIButton buttonWithType:UIButtonTypeCustom];
128 | }
129 | return _icon;
130 | }
131 |
132 | - (UILabel *)la{
133 | if (!_la) {
134 | _la = [UILabel new];
135 | }
136 | return _la;
137 | }
138 |
139 | - (UIButton *)check{
140 | if (!_check) {
141 | _check = [UIButton buttonWithType:UIButtonTypeCustom];
142 | }
143 | return _check;
144 | }
145 |
146 | @end
147 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## UI效果仿照前端element-UI的[Tree控件](https://element.eleme.cn/#/zh-CN/component/tree)
2 | [](https://developer.apple.com/iphone/index.action)
3 | [](https://img.shields.io/cocoapods/v/WMZTreeView.svg)
4 | [](http://mit-license.org)
5 |
6 | # **视图层**
7 |
8 | ### 正常树形显示
9 |
10 | 这里显示十级 每级100条数据 总共1000条数据的效果图
11 |
12 | 
13 |
14 | ```
15 | WMZTreeViewParam *param =TreeViewParam() .wDataSet(@[TreeParam(),TreeParam()])
16 | self.treeView = [[WMZTreeView alloc]initWithParam:param];
17 | [self.view addSubview:self.treeView];
18 | ```
19 | ### 可选中树形+选中高亮显示
20 |
21 | 
22 |
23 | ```
24 | TreeViewParam()
25 | //可勾选
26 | .wShowCheckboxSet(YES)
27 | //节点字体高亮颜色
28 | .wHighlightCurrentSet(TreeColor(0x1d76db))
29 | ```
30 |
31 | ### 自定义节点内容+增删节点
32 |
33 | 
34 |
35 |
36 | ```
37 | TreeViewParam()
38 | //自定义节点内容
39 | .wEventTreeCellSet(^UITableViewCell *(id model, NSIndexPath *path,UITableView *table,id param) {
40 | WMZMyCell *cell = [table dequeueReusableCellWithIdentifier:@"WMZMyCell"];
41 | if (!cell) {
42 | cell = [[WMZMyCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"WMZMyCell" parentModel:param];
43 | }
44 | cell.model = model;
45 | return cell;
46 | })
47 |
48 | 增删实例方法
49 |
50 | /*
51 | *为 Tree 中的一个节点追加一个子节点
52 | @param currrentID 当前节点
53 | @param param 子节点数据
54 | @return BOOL 是否追加成功
55 | */
56 | - (BOOL)append:(NSString*)currrentID node:(WMZTreeParam*)param;
57 |
58 | /*
59 | *为 Tree 的一个节点的后面增加一个节点
60 | @param currrentID 当前节点
61 | @param param 子节点数据
62 | @return BOOL 是否追加成功
63 | */
64 | - (BOOL)insertAfter:(NSString*)currrentID node:(WMZTreeParam*)param;
65 |
66 | /*
67 | *为 Tree 的一个节点的前面增加一个节点
68 | @param currrentID 当前节点
69 | @param param 子节点数据
70 | @return BOOL 是否追加成功
71 | */
72 | - (BOOL)insertBefore:(NSString*)currrentID node:(WMZTreeParam*)param;
73 |
74 | /*
75 | *删除节点
76 | @param currrentID 当前节点
77 | @return BOOL 是否追加成功
78 | */
79 | - (BOOL)remove:(NSString*)currrentID;
80 |
81 |
82 | ```
83 |
84 | ### 手风琴效果+指定层级可勾选 (这里选取3级结构,指定最后一级才可勾选)
85 |
86 | 
87 |
88 |
89 | ```
90 | //手风琴效果 同级只展开一级
91 | TreeViewParam().wAccordionSet(YES)
92 |
93 | //能否选择
94 | TreeParam().canSelectSet(NO)
95 | ```
96 | ### 勾选不关联父节点和子节点+默认选中+默认全部展开
97 |
98 | 
99 |
100 | ```
101 | TreeViewParam()
102 | //父节点和子节点 勾选不关联
103 | .wCheckStrictlySet(NO)
104 | //默认展开全部
105 | .wDefaultExpandAllSet(YES)
106 | //默认勾选
107 | .wDefaultExpandedKeysSet(@[@"5",@"10",@"12"])
108 |
109 | ```
110 | ### 开启拖拽
111 | 
112 |
113 | ```
114 | TreeViewParam()
115 | //拖拽
116 | .wDraggableSet(YES)
117 | ```
118 |
119 | # **模型层**
120 | 1 任意模型实现WMZTreeProcotol协议
121 | ```
122 | 参考WMZCustomModel
123 | ```
124 | 1.使用或继承WMZTreeParam (已经实现了WMZTreeProcotol协议)
125 | ```
126 | WMZTreeParam *tree = WMZTreeParam.new;
127 | tree.cueerntId = @"1";
128 | tree.parentId = @"2";
129 | tree.name = @"第一级";
130 | ```
131 |
132 | 2.NSDictionary
133 | ```
134 | @[
135 | @{
136 | WMZTreeName:@"1级",
137 | WMZTreeCurrentId:@"1",
138 | WMZTreeChildren:@[
139 | @{
140 | WMZTreeName:@"1_2_1级",
141 | WMZTreeCurrentId:@"1_2_1",
142 | WMZTreeParentId:@"1",
143 | WMZTreeChildren:@[
144 | @{
145 | WMZTreeName:@"1_3_1级",
146 | WMZTreeCurrentId:@"1_3_1",
147 | WMZTreeParentId:@"1_2_1",
148 | },
149 | @{
150 | WMZTreeName:@"1_3_2级",
151 | WMZTreeCurrentId:@"1_3_2",
152 | WMZTreeParentId:@"1_2_1",
153 | },
154 | @{
155 | WMZTreeName:@"1_3_3级",
156 | WMZTreeCurrentId:@"1_3_3",
157 | WMZTreeParentId:@"1_2_1",
158 | },
159 | ]
160 | },
161 | @{
162 | WMZTreeName:@"1_2_2级",
163 | WMZTreeCurrentId:@"1_2_2",
164 | WMZTreeParentId:@"1",
165 | },
166 | @{
167 | WMZTreeName:@"1_2_3级",
168 | WMZTreeCurrentId:@"1_2_3",
169 | WMZTreeParentId:@"1",
170 | },
171 | ]
172 | },
173 | @{
174 | WMZTreeName:@"2级",
175 | WMZTreeCurrentId:@"2",
176 | WMZTreeChildren:@[
177 | @{
178 | WMZTreeName:@"2_2_1级",
179 | WMZTreeCurrentId:@"2_2_1",
180 | WMZTreeParentId:@"2",
181 | },
182 | @{
183 | WMZTreeName:@"2_2_2级",
184 | WMZTreeCurrentId:@"2_2_2",
185 | WMZTreeParentId:@"2",
186 | },
187 | @{
188 | WMZTreeName:@"2_2_3级",
189 | WMZTreeCurrentId:@"2_2_3",
190 | WMZTreeParentId:@"2",
191 | },
192 | ]
193 | },
194 | @{
195 | WMZTreeName:@"3级",
196 | WMZTreeCurrentId:@"3",
197 | WMZTreeChildren:@[
198 | @{
199 | WMZTreeName:@"3_2_1级",
200 | WMZTreeCurrentId:@"3_2_1",
201 | WMZTreeParentId:@"3",
202 | },
203 | @{
204 | WMZTreeName:@"3_2_2级",
205 | WMZTreeCurrentId:@"3_2_2",
206 | WMZTreeParentId:@"3",
207 | },
208 | @{
209 | WMZTreeName:@"3_2_3级",
210 | WMZTreeCurrentId:@"3_2_3",
211 | WMZTreeParentId:@"3",
212 | },
213 | ]
214 | }
215 | ]
216 | ```
217 |
218 | ## 配置参数
219 | | 可配置参数 | 类型 | 作用 |
220 | |------------------------|-----------|--------------------------------------------------------|
221 | | cueerntId | NSString | 当前节点ID 必传 |
222 | | parentId | NSString | 父节点ID,不传表示第一级 |
223 | | name | NSString | 显示的文本 |
224 | | isExpand | BOOL | 是否展开 默认NO |
225 | | canSelect | BOOL | 能否选中 默认NO |
226 | | data | id | 携带的其他数据|
227 |
228 | ### 其他具体看demo
229 |
230 | ### 依赖
231 | 无任何依赖
232 |
233 | 安装
234 | ==============
235 |
236 | ### CocoaPods
237 | 1. 将 cocoapods 更新至最新版本.
238 | 2. 在 Podfile 中添加 `pod 'WMZTreeView'`。
239 | 3. 执行 `pod install` 或 `pod update`。
240 | 4. 导入 #import "WMZTreeView.h"。
241 |
242 | ### 手动安装
243 |
244 | 1. 下载 WMZTreeView 文件夹内的所有内容。
245 | 2. 将 WMZTreeView 内的源文件添加(拖放)到你的工程。
246 | 3. 导入 #import "WMZTreeView.h"
247 |
248 | 系统要求
249 | ==============
250 | 该库最低支持 `iOS 9.0` 和 `Xcode 9.0`。
251 |
252 |
253 |
254 | 许可证
255 | ==============
256 | 使用 MIT 许可证,详情见 [LICENSE](LICENSE) 文件。
257 |
258 |
259 | 个人主页
260 | ==============
261 | 使用过程中如果有什么bug欢迎给我提issue 我看到就会解决
262 | [简书地址](https://www.jianshu.com/p/dedf610739be)
263 |
--------------------------------------------------------------------------------
/WMZTreeView/WMZTreeView/WMZTreeBaseView.m:
--------------------------------------------------------------------------------
1 | //
2 | // WMZTreeBaseView.m
3 | // WMZTree
4 | //
5 | // Created by wmz on 2019/10/19.
6 | // Copyright © 2019 wmz. All rights reserved.
7 | //
8 |
9 | #import "WMZTreeBaseView.h"
10 |
11 | @interface WMZTreeBaseView()
12 | /// 已展开的树形数组
13 | @property(nonatomic, strong, readwrite) NSMutableArray *>*data;
14 | /// 全部字典
15 | @property(nonatomic, strong, readwrite) NSMutableDictionary *>*dic;
16 | /// 储存的model字典
17 | @property(nonatomic, strong, readwrite) NSMutableDictionary *>*tmpDic;
18 | /// 数据为空的占位显示图
19 | @property(nonatomic, strong, readwrite) UIView *emptyView;
20 |
21 | @end
22 |
23 | @implementation WMZTreeBaseView
24 |
25 | /// 处理空视图
26 | - (void)setUpEmptyView:(NSDictionary*)dic{
27 | UIImageView *image = (UIImageView*)[self.emptyView viewWithTag:999];
28 | UILabel *la = (UILabel*)[self.emptyView viewWithTag:998];
29 | la.center = self.emptyView.center;
30 | image.frame = CGRectMake(image.frame.origin.x, CGRectGetMinY(la.frame)-image.frame.size.height, image.frame.size.width, image.frame.size.height);
31 | image.center = CGPointMake(self.emptyView.center.x, image.center.y);
32 | if (dic[WMZTreeImage]) image.image = [UIImage imageNamed:dic[WMZTreeImage]];
33 | if (dic[WMZTreeName]) la.text = dic[WMZTreeName];
34 | }
35 |
36 | - (NSObject*)dictionaryToParam:(NSDictionary*)dic{
37 | WMZTreeParam *param = WMZTreeParam.new;
38 | if (dic[WMZTreeName]) param.name = dic[WMZTreeName];
39 | if (dic[WMZTreeCurrentId]) param.currentId = dic[WMZTreeCurrentId];
40 | if (dic[WMZTreeParentId]) param.parentId = dic[WMZTreeParentId];
41 | if (dic[WMZTreeExpand]) param.isExpand = [dic[WMZTreeExpand] boolValue];
42 | if (dic[WMZTreeCanSelect]) param.canSelect = [dic[WMZTreeCanSelect] boolValue];
43 | if (dic[WMZTreeExistData]) param.data = dic[WMZTreeExistData];
44 | if (dic[WMZTreeChildren]) param.children = [NSMutableArray arrayWithArray:dic[WMZTreeChildren]];
45 | if (param.currentId) {
46 | [self.dic setObject:param forKey:param.currentId];
47 | if (!param.parentId && param) [self.tree.children addObject:param];
48 | }
49 | return param;
50 | }
51 |
52 | /// 寻找所有子节点
53 | - (NSMutableArray*)getSonData:(NSObject*)node type:(TreeDataType)type{
54 | NSMutableArray *sonData = [NSMutableArray new];
55 | if (!node) return sonData;
56 | NSMutableArray *stack = [NSMutableArray new];
57 | [stack addObject:node];
58 | NSObject *tmpNode = [NSObject new];
59 | while (stack.count) {
60 | NSObject *son = nil;
61 | tmpNode = stack.lastObject;
62 | NSObject *parentNode = self.dic[tmpNode.parentId];
63 | [stack removeLastObject];
64 | if (!tmpNode.depath &&
65 | tmpNode!=node) {
66 | tmpNode.depath = parentNode.depath + 1;
67 | }
68 | if (type == TreeDataAllWithSelf) {
69 | if (parentNode.isExpand) {
70 | if(tmpNode) [sonData addObject:tmpNode];
71 | }else{
72 | if (parentNode.isExpand&&[sonData indexOfObject:parentNode]!=NSNotFound) {
73 | if(tmpNode) [sonData addObject:tmpNode];
74 | }
75 | }
76 | }else if (type == TreeDataAll) {
77 | if (tmpNode!=node) {
78 | tmpNode.isExpand = YES;
79 | if(tmpNode) [sonData addObject:tmpNode];
80 | }
81 | }else if (type == TreeDataGetSelectAll) {
82 | if (tmpNode!=node) {
83 | if(tmpNode) [sonData addObject:tmpNode];
84 | }
85 | }else if (type == TreeDataSelectAll) {
86 | if (tmpNode!=node) {
87 | tmpNode.isSelected = node.isSelected;
88 | if (!node.isSelected) {
89 | tmpNode.halfSelect = NO;
90 | }
91 | }
92 | }else if (type == TreeDataExpandOrNotParent) {
93 | if (tmpNode!=node) {
94 | if (!tmpNode.parentId) {
95 | if(tmpNode) [sonData addObject:tmpNode];
96 | }else if (([sonData indexOfObject:parentNode]!=NSNotFound)&&parentNode.isExpand){
97 | if(tmpNode) [sonData addObject:tmpNode];
98 | }
99 | }
100 | }else if(type == TreeDataDelete || type == TreeDataInsert){
101 | if (tmpNode!=node) {
102 | if ([tmpNode.parentId isEqualToString: node.currentId]) {
103 | if (type == TreeDataInsert?(parentNode.isExpand):(!parentNode.isExpand)) {
104 | if(tmpNode) [sonData addObject:tmpNode];
105 | }
106 | }else{
107 | if (parentNode.isExpand&&
108 | [sonData indexOfObject:parentNode]!=NSNotFound) {
109 | if(tmpNode) [sonData addObject:tmpNode];
110 | }
111 | }
112 | }
113 | }else if(type == TreeDataSameLevel){
114 | if (parentNode) {
115 | for (NSInteger i = 0; i < parentNode.children.count; i++) {
116 | son = parentNode.children[i];
117 | if (son!=tmpNode) {
118 | if(son) [sonData addObject:son];
119 | }
120 | }
121 | break;
122 | }
123 | }else{
124 | if (tmpNode!=node) {
125 | if (tmpNode) [sonData addObject:tmpNode];
126 | }
127 | }
128 |
129 | for (NSInteger i = tmpNode.children.count - 1; i >= 0; i--) {
130 | son = tmpNode.children[i];
131 | if(son) [stack addObject:son];
132 | }
133 | }
134 | return sonData;
135 | }
136 |
137 | /// 寻找该节点的所有父节点
138 | - (NSArray*)searchAllParentNode:(NSObject *)param{
139 | NSMutableArray *arr = [NSMutableArray new];
140 | NSMutableArray *loop= [NSMutableArray new];
141 | if (param.parentId &&
142 | self.dic[param.parentId])
143 | [loop addObject:self.dic[param.parentId]];
144 |
145 | while (loop.count) {
146 | NSObject *tmp = loop.lastObject;
147 | if (param.isSelected) {
148 | tmp.halfSelect = NO;
149 | tmp.isSelected = YES;
150 | for (NSObject *son in tmp.children) {
151 | if (!son.isSelected&&
152 | son.canSelect) {
153 | tmp.halfSelect = YES;
154 | tmp.isSelected = NO;
155 | break;
156 | }
157 | }
158 | }else{
159 | tmp.halfSelect = NO;
160 | tmp.isSelected = NO;
161 | for (NSObject *son in tmp.children) {
162 | if (son!=tmp&&
163 | son.isSelected&&
164 | son.canSelect) {
165 | tmp.halfSelect = YES;
166 | break;
167 | }
168 | }
169 | }
170 | [loop removeLastObject];
171 | if(tmp) [arr addObject:tmp];
172 | if (tmp.parentId &&
173 | self.dic[tmp.parentId]) {
174 | [loop addObject:self.dic[tmp.parentId]];
175 | }
176 | }
177 | return arr;
178 | }
179 |
180 | - (UIView *)emptyView{
181 | if (!_emptyView) {
182 | _emptyView = [UIView new];
183 | _emptyView.frame = self.bounds;
184 | UIImageView *image = [UIImageView new];
185 | image.contentMode = UIViewContentModeScaleAspectFit;
186 | image.tag = 999;
187 | image.frame = CGRectMake(0, 0, _emptyView.bounds.size.width<200?_emptyView.bounds.size.width*0.8:200, _emptyView.bounds.size.height<200?_emptyView.bounds.size.height*0.8:200);
188 | [_emptyView addSubview:image];
189 | UILabel *la = [UILabel new];
190 | la.tag = 998;
191 | la.textAlignment = NSTextAlignmentCenter;
192 | la.frame = CGRectMake(0, 0, _emptyView.bounds.size.width, 40);
193 | [_emptyView addSubview:la];
194 | }
195 | return _emptyView;
196 | }
197 |
198 | - (UITableView *)table{
199 | if (!_table) {
200 | _table = [[UITableView alloc]initWithFrame:self.bounds style:UITableViewStyleGrouped];
201 | _table.estimatedRowHeight = 100;
202 | if (@available(iOS 11.0, *)) {
203 | _table.estimatedSectionFooterHeight = 0;
204 | _table.estimatedSectionHeaderHeight = 0;
205 | _table.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
206 | }
207 | #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 150000
208 | if (@available(iOS 15.0, *)) {
209 | _table.sectionHeaderTopPadding = 0;
210 | }
211 | #endif
212 | _table.backgroundColor = UIColor.clearColor;
213 | _table.delegate = (id)self;
214 | _table.dataSource = (id)self;
215 | }
216 | return _table;
217 | }
218 |
219 | - (NSMutableArray *> *)data{
220 | if (!_data) {
221 | _data = [NSMutableArray new];
222 | }
223 | return _data;
224 | }
225 |
226 | - (NSMutableDictionary *> *)dic{
227 | if (!_dic) {
228 | _dic = NSMutableDictionary.new;
229 | }
230 | return _dic;
231 | }
232 |
233 | - (NSMutableDictionary *> *)tmpDic{
234 | if (!_tmpDic) {
235 | _tmpDic = NSMutableDictionary.new;
236 | }
237 | return _tmpDic;
238 | }
239 | @end
240 |
--------------------------------------------------------------------------------
/WMZTreeView/ViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.m
3 | // WMZTree
4 | //
5 | // Created by wmz on 2019/10/17.
6 | // Copyright © 2019 wmz. All rights reserved.
7 | //
8 |
9 | #import "ViewController.h"
10 | #import "WMZTreeView.h"
11 | #import "WMZMyCell.h"
12 | #import "WMZCustomModel.h"
13 |
14 | @interface ViewController ()
15 | @property(nonatomic,strong)WMZTreeView *treeView;
16 | @end
17 |
18 | @implementation ViewController
19 |
20 | - (void)viewDidLoad {
21 | [super viewDidLoad];
22 | self.view.backgroundColor = [UIColor whiteColor];
23 |
24 | NSDictionary *dic = @{
25 | @(1):@"firstDemo",
26 | @(2):@"secondDemo",
27 | @(3):@"thirdDemo",
28 | @(4):@"fourDemo",
29 | @(5):@"fiveDemo",
30 | @(6):@"sixDemo",
31 | @(7):@"sevenDemo",
32 | @(8):@"eightDemo",
33 | };
34 | if (self.type) {
35 | [self performSelector:NSSelectorFromString(dic[@(self.type)]) withObject:nil afterDelay:0.01];
36 | }
37 |
38 | }
39 |
40 | //全部属性
41 | - (void)all{
42 | WMZTreeViewParam *param =
43 | TreeViewParam()
44 | .wDataSet([self randomArr:20 level:10])
45 | // .wDataSet([self jsonData])
46 | //数据为空时的占位图
47 | .wEmptyDataSet(@{WMZTreeName:@"暂无数据",WMZTreeImage:@"default_maintenance"})
48 | //frame
49 | .wFrameSet(CGRectMake(0, 88, self.view.bounds.size.width, self.view.bounds.size.height-88))
50 | //缩进距离
51 | .wIndentSet(2)
52 | //手风琴效果 同级只展开一级
53 | .wAccordionSet(NO)
54 | //可勾选
55 | .wShowCheckboxSet(YES)
56 | //隐藏展开图标
57 | .wHideExpanIconSet(NO)
58 | //节点字体font
59 | .wNodeTextFontSet(15.0f)
60 | //节点字体颜色
61 | .wNodeTextColorSet([UIColor blackColor])
62 | //节点字体高亮颜色
63 | .wHighlightCurrentSet(TreeColor(0x1d76db))
64 | //默认勾选
65 | .wDefaultExpandedKeysSet(@[@"1_2_1",@"2"])
66 | //父节点和子节点 勾选关联
67 | .wCheckStrictlySet(YES)
68 | //默认展开全部
69 | .wDefaultExpandAllSet(YES)
70 | //拖拽
71 | .wDraggableSet(NO)
72 | //节点点击事件
73 | .wEventNodeClickSet(^(id node) {
74 | NSLog(@"%@被点击",node);
75 | })
76 | //节点勾选状态切换事件
77 | .wEventCheckChangeSet(^(id node, BOOL isSelect) {
78 | NSLog(@"节点切换 %@ , %d",node,isSelect);
79 | })
80 | //自定义节点内容
81 | .wEventTreeCellSet(^UITableViewCell *(id model, NSIndexPath *path,UITableView *table,id param) {
82 | WMZMyCell *cell = [table dequeueReusableCellWithIdentifier:@"WMZMyCell"];
83 | if (!cell) {
84 | cell = [[WMZMyCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"WMZMyCell" parentModel:param];
85 | }
86 | cell.model = model;
87 | return cell;
88 | })
89 | //自定义节点高度
90 | .wEventCellHeightSet(^CGFloat(id model, NSIndexPath *path, UITableView *table) {
91 | return 50;
92 | })
93 | //自定义节点用户交互
94 | .wEventCellUserEnabledSet(^(id model, NSIndexPath *path, UITableView *table,id userInfo) {
95 |
96 | })
97 | //节点拖拽完成
98 | .wEventNodeDraggableSet(^(NSIndexPath *sourceIndexPath, NSIndexPath *destinationIndexPath, UITableView *table) {
99 |
100 | });
101 | self.treeView = [[WMZTreeView alloc]initWithParam:param];
102 | [self.view addSubview:self.treeView];
103 | }
104 |
105 |
106 | //正常多叉树显示
107 | - (void)firstDemo{
108 | WMZTreeViewParam *param =TreeViewParam()
109 | .wFrameSet(CGRectMake(0, 88, self.view.bounds.size.width, self.view.bounds.size.height-88))
110 | .wDataSet([self randomArr:100 level:10]);
111 | self.treeView = [[WMZTreeView alloc]initWithParam:param];
112 | [self.view addSubview:self.treeView];
113 | }
114 |
115 | //可选中树形+选中高亮显示
116 | - (void)secondDemo{
117 | WMZTreeViewParam *param =TreeViewParam()
118 | .wDataSet([self randomArr:10 level:5])
119 | .wFrameSet(CGRectMake(0, 88, self.view.bounds.size.width, self.view.bounds.size.height-88))
120 | //节点字体高亮颜色
121 | .wHighlightCurrentSet(TreeColor(0x1d76db))
122 | //可勾选
123 | .wShowCheckboxSet(YES);
124 | self.treeView = [[WMZTreeView alloc]initWithParam:param];
125 | [self.view addSubview:self.treeView];
126 | }
127 |
128 | //自定义节点内容+增删节点
129 | - (void)thirdDemo{
130 | __weak ViewController *weakSelf = self;
131 | WMZTreeViewParam *param =TreeViewParam()
132 | .wFrameSet(CGRectMake(0, 88, self.view.bounds.size.width, self.view.bounds.size.height-88))
133 | .wDataSet([self randomArr:10 level:5])
134 | //自定义节点内容
135 | .wEventTreeCellSet(^UITableViewCell *(id model, NSIndexPath *path,UITableView *table,id param) {
136 | WMZMyCell *cell = [table dequeueReusableCellWithIdentifier:@"WMZMyCell"];
137 | if (!cell) {
138 | cell = [[WMZMyCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"WMZMyCell" parentModel:param];
139 | }
140 | cell.model = model;
141 | return cell;
142 | })
143 | //自定义节点用户交互
144 | .wEventCellUserEnabledSet(^(id model, NSIndexPath *path, UITableView *table,id userInfo) {
145 | [weakSelf dealModel:model path:path userInfo:userInfo];
146 | });
147 | self.treeView = [[WMZTreeView alloc]initWithParam:param];
148 | [self.view addSubview:self.treeView];
149 | }
150 |
151 | //手风琴效果+指定层级可勾选(这里选取3层 指定第三层可选)
152 | - (void)fourDemo{
153 | NSArray *data = @[
154 | ///第一层
155 | [WMZTreeParam initWithName:@"第1_0级" currentId:@"1" parentId:nil canSelect:NO],
156 | [WMZTreeParam initWithName:@"第1_1级" currentId:@"2" parentId:nil canSelect:NO],
157 | [WMZTreeParam initWithName:@"第1_2级" currentId:@"3" parentId:nil canSelect:NO],
158 | ///第二层
159 | [WMZTreeParam initWithName:@"第2_11级" currentId:@"11" parentId:@"1" canSelect:NO],
160 | [WMZTreeParam initWithName:@"第2_22级" currentId:@"22" parentId:@"2" canSelect:NO],
161 | [WMZTreeParam initWithName:@"第2_22级" currentId:@"33" parentId:@"3" canSelect:NO],
162 | ///第三层可选
163 | [WMZTreeParam initWithName:@"第3_111级" currentId:@"111" parentId:@"11"],
164 | [WMZTreeParam initWithName:@"第3_222级" currentId:@"222" parentId:@"22"],
165 | [WMZTreeParam initWithName:@"第3_333级" currentId:@"333" parentId:@"33"],
166 | ];
167 | WMZTreeViewParam *param =TreeViewParam()
168 | // .wHideExpanIconSet(YES)
169 | .wFrameSet(CGRectMake(0, 88, self.view.bounds.size.width, self.view.bounds.size.height-88))
170 | .wDataSet(data)
171 | //展示 不可选
172 | .wShowOnlySet(YES)
173 | //可勾选
174 | .wShowCheckboxSet(YES)
175 | //手风琴效果
176 | .wAccordionSet(YES);
177 | self.treeView = [[WMZTreeView alloc]initWithParam:param];
178 | [self.view addSubview:self.treeView];
179 | }
180 |
181 | //勾选不关联父节点和子节点+默认选中+默认全部展开
182 | - (void)fiveDemo{
183 | WMZTreeViewParam *param =TreeViewParam()
184 | .wDataSet(([self randomArr:10 level:5]))
185 | .wFrameSet(CGRectMake(0, 88, self.view.bounds.size.width, self.view.bounds.size.height-88))
186 | //默认全部展开
187 | .wDefaultExpandAllSet(YES)
188 | //不关联父节点和子节点
189 | .wCheckStrictlySet(YES)
190 | //可勾选
191 | .wShowCheckboxSet(YES)
192 | //默认选中
193 | .wDefaultExpandedKeysSet(@[@"5",@"8"]);
194 | self.treeView = [[WMZTreeView alloc]initWithParam:param];
195 | [self.view addSubview:self.treeView];
196 | }
197 |
198 | //开启拖拽
199 | - (void)sixDemo{
200 | WMZTreeViewParam *param =TreeViewParam()
201 | .wFrameSet(CGRectMake(0, 88, self.view.bounds.size.width, self.view.bounds.size.height-88))
202 | .wDefaultExpandAllSet(YES)
203 | .wShowCheckboxSet(YES)
204 | .wDataSet([self randomArr:20 level:5]);
205 |
206 | self.treeView = [[WMZTreeView alloc]initWithParam:param];
207 | [self.view addSubview:self.treeView];
208 |
209 |
210 | UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
211 | [btn setTitleColor:TreeColor(0xF4606C) forState:UIControlStateNormal];
212 | [btn setTitle:@"开启拖拽" forState:UIControlStateNormal];
213 | [btn addTarget:self action:@selector(onBtnAction:) forControlEvents:UIControlEventTouchUpInside];
214 | UIBarButtonItem *barItem = [[UIBarButtonItem alloc] initWithCustomView:btn];
215 | self.navigationItem.rightBarButtonItem = barItem;
216 | }
217 |
218 | //传字典数据
219 | - (void)sevenDemo{
220 | WMZTreeViewParam *param =TreeViewParam()
221 | .wFrameSet(CGRectMake(0, 88, self.view.bounds.size.width, self.view.bounds.size.height-88))
222 | .wShowCheckboxSet(YES)
223 | .wDataSet([self jsonData]);
224 | self.treeView = [[WMZTreeView alloc]initWithParam:param];
225 | [self.view addSubview:self.treeView];
226 | }
227 |
228 | ///实现协议的数据
229 | -(void)eightDemo{
230 | WMZTreeViewParam *param =TreeViewParam()
231 | .wFrameSet(CGRectMake(0, 88, self.view.bounds.size.width, self.view.bounds.size.height-88))
232 | .wShowCheckboxSet(YES)
233 | .wDataSet([self protocolArr]);
234 | self.treeView = [[WMZTreeView alloc]initWithParam:param];
235 | [self.view addSubview:self.treeView];
236 | }
237 |
238 | //增删数据
239 | - (void)dealModel:(id)model path:(NSIndexPath*)path userInfo:(id)userInfo{
240 | if ([userInfo isEqualToString:@"add"]) {
241 | WMZTreeParam *param = model;
242 | __weak ViewController *weakSelf = self;
243 | UIAlertController *alertController = [UIAlertController alertControllerWithTitle:nil message:@"请输入节点" preferredStyle:UIAlertControllerStyleAlert];
244 | [alertController addAction:[UIAlertAction actionWithTitle:@"追加子节点" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
245 | UITextField *idText = alertController.textFields[0];
246 | UITextField *nameText = alertController.textFields[1];
247 | WMZTreeParam *node = [WMZTreeParam initWithName:nameText.text currentId:idText.text parentId:param.currentId];
248 | [weakSelf.treeView append:param.currentId node:node];
249 | }]];
250 | [alertController addAction:[UIAlertAction actionWithTitle:@"追加节点" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
251 | UITextField *idText = alertController.textFields[0];
252 | UITextField *nameText = alertController.textFields[1];
253 | WMZTreeParam *node = [WMZTreeParam initWithName:nameText.text currentId:idText.text parentId:param.parentId];
254 | [weakSelf.treeView insertAfter:param.currentId node:node];
255 | }]];
256 | [alertController addAction:[UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:nil]];
257 |
258 | [alertController addTextFieldWithConfigurationHandler:^(UITextField*_Nonnull textField) {
259 | textField.placeholder=@"请输入节点唯一currentId";
260 | }];
261 |
262 | [alertController addTextFieldWithConfigurationHandler:^(UITextField*_Nonnull textField) {
263 | textField.placeholder=@"请输入节点name";
264 | }];
265 | [self presentViewController:alertController animated:YES completion:nil];
266 | }else if ([userInfo isEqualToString:@"delete"]){
267 | WMZTreeParam *param = model;
268 | [self.treeView remove:param.currentId];
269 | }
270 | }
271 |
272 | ///随机多少级 每级多少条数据
273 | - (NSArray*)randomArr:(int)num level:(int)level{
274 | NSMutableArray *arr = [NSMutableArray new];
275 | NSArray *firstId = @[@"0",@"1",@"2"];
276 | ///第一级
277 | for (int i = 0; i*)protocolArr{
316 | NSMutableArray *marr = NSMutableArray.new;
317 | WMZCustomModel *model = WMZCustomModel.new;
318 | model.name = @"model1";
319 | model.currentId = @"1";
320 | [marr addObject:model];
321 |
322 | model = WMZCustomModel.new;
323 | model.name = @"model2";
324 | model.currentId = @"2";
325 | [marr addObject:model];
326 |
327 | model = WMZCustomModel.new;
328 | model.name = @"model1_1";
329 | model.currentId = @"1_1";
330 | model.parentId = @"1";
331 | [marr addObject:model];
332 |
333 | model = WMZCustomModel.new;
334 | model.name = @"model2_2";
335 | model.currentId = @"12_2";
336 | model.parentId = @"2";
337 | [marr addObject:model];
338 | return [NSArray arrayWithArray:marr];
339 | }
340 |
341 | ///json数据
342 | - (NSArray*)jsonData{
343 | return @[
344 | @{
345 | WMZTreeName:@"1级",
346 | WMZTreeCurrentId:@"1",
347 | WMZTreeChildren:@[
348 | @{
349 | WMZTreeName:@"1_2_1级",
350 | WMZTreeCurrentId:@"1_2_1",
351 | WMZTreeParentId:@"1",
352 | WMZTreeChildren:@[
353 | @{
354 | WMZTreeName:@"1_3_1级",
355 | WMZTreeCurrentId:@"1_3_1",
356 | WMZTreeParentId:@"1_2_1",
357 | },
358 | @{
359 | WMZTreeName:@"1_3_2级",
360 | WMZTreeCurrentId:@"1_3_2",
361 | WMZTreeParentId:@"1_2_1",
362 | },
363 | ]
364 | },
365 | @{
366 | WMZTreeName:@"1_2_2级",
367 | WMZTreeCurrentId:@"1_2_2",
368 | WMZTreeParentId:@"1",
369 | },
370 | ]
371 | },
372 | @{
373 | WMZTreeName:@"2级",
374 | WMZTreeCurrentId:@"2",
375 | WMZTreeChildren:@[
376 | @{
377 | WMZTreeName:@"2_2_1级",
378 | WMZTreeCurrentId:@"2_2_1",
379 | WMZTreeParentId:@"2",
380 | },
381 | ]
382 | },
383 | ];
384 | }
385 |
386 | @end
387 |
--------------------------------------------------------------------------------
/WMZTreeView/WMZTreeView/WMZTreeView.m:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | //
5 | // WMZTreeView.m
6 | // WMZTree
7 | //
8 | // Created by wmz on 2019/10/17.
9 | // Copyright © 2019 wmz. All rights reserved.
10 | //
11 |
12 | #import "WMZTreeView.h"
13 | #import "WMZTreeCustomCell.h"
14 | @interface WMZTreeView()
15 | @end
16 | @implementation WMZTreeView
17 |
18 | - (instancetype)initWithParam:(WMZTreeViewParam*)param{
19 | if (self = [super init]) {
20 | self.param = param;
21 | self.frame = self.param.wFrame;
22 | [self setUp];
23 | }
24 | return self;
25 | }
26 |
27 | /// 更新编辑状态
28 | - (void)updateEditing{
29 | self.table.editing = self.param.wDraggable;
30 | [self.table reloadData];
31 | }
32 |
33 | /// 全选
34 | - (void)selectAll{
35 | NSMutableArray *insetArr = [self getSonData:self.tree type:TreeDataGetSelectAll];
36 | for (NSObject *param in insetArr) {
37 | if (param.canSelect) param.isSelected = YES;
38 | }
39 | [self.table reloadData];
40 | }
41 |
42 | /// 全部取消选中
43 | - (void)notSelectAll{
44 | NSMutableArray *insetArr = [self getSonData:self.tree type:TreeDataGetSelectAll];
45 | for (NSObject *param in insetArr) {
46 | if (param.canSelect) param.isSelected = NO;
47 | }
48 | [self.table reloadData];
49 | }
50 |
51 | - (void)setUp{
52 | if (!self.param.wData||
53 | ![self.param.wData isKindOfClass:[NSArray class]]) return;
54 | NSArray *tmpArr = [NSArray arrayWithArray:self.param.wData];
55 | BOOL JSON = NO;
56 | for (id model in self.param.wData) {
57 | if ([model isKindOfClass:[NSDictionary class]]) {
58 | JSON = YES;
59 | break;
60 | }
61 | }
62 | self.tree = WMZTreeParam.new;
63 | if (JSON) {
64 | @autoreleasepool {
65 | [self changeJSONtToTreeModel:tmpArr type:self.param.wDefaultExpandAll?TreeDataAll: TreeDataExpandOrNotParent];
66 | }
67 | }else{
68 | @autoreleasepool {
69 | [self dealTreeData:tmpArr];
70 | }
71 | [self.data addObjectsFromArray:[self getSonData:self.tree type:self.param.wDefaultExpandAll?TreeDataAll: TreeDataExpandOrNotParent]];
72 | }
73 | if (!self.data.count) {
74 | [self.table removeFromSuperview];
75 | [self addSubview:self.emptyView];
76 | [self setUpEmptyView:self.param.wEmptyData];
77 | }else{
78 | [self.emptyView removeFromSuperview];
79 | [self addSubview:self.table];
80 | self.table.editing = self.param.wDraggable;
81 | }
82 | if (self.param.wDefaultExpandedKeys) {
83 | for (NSString *key in self.param.wDefaultExpandedKeys) {
84 | NSObject *value = self.dic[key];
85 | if (value) {
86 | value.isSelected = YES;
87 | if (self.param.wCheckStrictly) {
88 | [self setSelectNodeParentAndSonNodeStatus:value checkStrictly:self.param.wCheckStrictly reload:NO];
89 | }
90 | }
91 | }
92 | }
93 | [self.table reloadData];
94 | }
95 |
96 | /// 解析传入的数组 传入的NSObject组成的数组
97 | - (void)dealTreeData:(NSArray*>*)items {
98 | [items enumerateObjectsUsingBlock:^(NSObject * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
99 | [self.dic setObject:obj forKey:obj.currentId];
100 | }];
101 |
102 | [items enumerateObjectsUsingBlock:^(NSObject* _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
103 | if (!obj.parentId) {
104 | if (obj &&
105 | [obj conformsToProtocol:@protocol(WMZTreeProcotol)] &&
106 | [self.tree.children indexOfObject:obj] == NSNotFound)
107 | [self.tree.children addObject:obj];
108 | }else{
109 | NSObject *param = self.dic[obj.parentId];
110 | if (obj && [obj conformsToProtocol:@protocol(WMZTreeProcotol)]){
111 | if(!param.children) param.children = NSMutableArray.new;
112 | if([param.children indexOfObject:obj] == NSNotFound) [param.children addObject:obj];
113 | }
114 | if (param){
115 | __block NSInteger canSelectCount = 0;
116 | [param.children enumerateObjectsUsingBlock:^(NSObject * _Nonnull sonObj, NSUInteger idx, BOOL * _Nonnull stop) {
117 | if (!sonObj.canSelect) canSelectCount += 1;
118 | }];
119 | if (canSelectCount &&
120 | canSelectCount == param.children.count) {
121 | if (param.canSelect) param.canSelect = NO;
122 | }
123 | }
124 | }
125 | }];
126 | }
127 |
128 | /// 传进来的是字典 转为树形模型
129 | - (void)changeJSONtToTreeModel:(NSArray*)data type:(TreeDataType)type{
130 | if (!data) return;
131 | NSMutableArray *stack = [NSMutableArray new];
132 | NSDictionary *dic = @{WMZTreeChildren:data};
133 | if(dic) [stack addObject:dic];
134 | id tmpDic ;
135 | while (stack.count) {
136 | tmpDic = stack.lastObject;
137 | NSObject *tmpNode = nil;
138 | if ([tmpDic isKindOfClass:[NSDictionary class]]) {
139 | tmpNode = [self dictionaryToParam:tmpDic];
140 | }else{
141 | tmpNode = tmpDic;
142 | }
143 | if (tmpNode.parentId) {
144 | NSObject *parentNode = self.dic[tmpNode.parentId];
145 | NSInteger index = [parentNode.children indexOfObject:tmpDic];
146 | parentNode.children[index] = tmpNode;
147 | }
148 | NSObject *parentNode = self.dic[tmpNode.parentId];
149 | if (!tmpNode.depath&&tmpDic!=dic) {
150 | tmpNode.depath = parentNode.depath+1;
151 | }
152 | [stack removeLastObject];
153 | if (tmpNode.currentId) {
154 | if (type == TreeDataAll) {
155 | if (tmpDic!=dic) {
156 | tmpNode.isExpand = YES;
157 | if (tmpNode) [self.data addObject:tmpNode];
158 | }
159 | }else if (type == TreeDataExpandOrNotParent) {
160 | if (self.data ) {
161 | if (!tmpNode.parentId && tmpNode) {
162 | [self.data addObject:tmpNode];
163 | }
164 | else if (([self.data indexOfObject:parentNode]!=NSNotFound)&&
165 | parentNode.isExpand &&
166 | tmpNode){
167 | [self.data addObject:tmpNode];
168 | }
169 | }
170 | }
171 | }
172 | for (NSInteger i = tmpNode.children.count - 1; i >= 0; i--) {
173 | if (tmpNode.children[i]) {
174 | [stack addObject:tmpNode.children[i]];
175 | }
176 | }
177 | }
178 | }
179 |
180 | - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
181 | NSObject *param = self.data[indexPath.row];
182 | if (self.param.wEventCellHeight) {
183 | return self.param.wEventCellHeight(param,indexPath,tableView);
184 | }
185 | return 44;
186 | }
187 |
188 | - (UIView*)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section{
189 | return nil;
190 | }
191 |
192 | - (UIView*)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section{
193 | return nil;
194 | }
195 |
196 | - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section{
197 | return 0.01;
198 | }
199 |
200 | - (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section{
201 | return 0.01;
202 | }
203 |
204 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
205 | return self.data.count;
206 | }
207 |
208 | -(UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath{
209 | return UITableViewCellEditingStyleNone;
210 | }
211 |
212 | -(void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath{
213 | NSObject *param = self.data[sourceIndexPath.row];
214 | NSObject *toParam = self.data[destinationIndexPath.row];
215 | if (param.parentId) {
216 | NSObject *parent = self.dic[param.parentId];
217 | [parent.children removeObject:param];
218 | }
219 | param.parentId = toParam.parentId;
220 | param.depath = toParam.depath;
221 | param.isExpand = NO;
222 | if (toParam.parentId) {
223 | NSObject *parent = self.dic[toParam.parentId];
224 | NSInteger index = [parent.children indexOfObject:toParam];
225 | if (index <= parent.children.count) {
226 | [parent.children insertObject:param atIndex:index];
227 | }
228 | }
229 | [self.data removeObject:param];
230 | NSMutableArray *paramArr = [self getSonData:param type:TreeDataGetSelectAll];
231 | [self.data insertObject:param atIndex:destinationIndexPath.row];
232 | int i = 0;
233 | for (NSObject *son in paramArr) {
234 | if([self.data indexOfObject:son] != NSNotFound){
235 | [self.data removeObject:son];
236 | [self.data insertObject:son atIndex:destinationIndexPath.row + i + 1];
237 | }
238 | i ++;
239 | }
240 | [tableView reloadData];
241 | if (self.param.wEventNodeDraggable) {
242 | self.param.wEventNodeDraggable(sourceIndexPath, destinationIndexPath, tableView);
243 | }
244 | }
245 |
246 | - (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath{
247 | return YES;
248 | }
249 |
250 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
251 | if (self.param.wEventTreeCell) {
252 | WMZTreeCustomCell *cell = (WMZTreeCustomCell*)self.param.wEventTreeCell(self.data[indexPath.row],indexPath,tableView,self.param);
253 | if ([cell isKindOfClass:WMZTreeCustomCell.class])
254 | cell.delagete = self;
255 |
256 | if (cell) return cell;
257 | }
258 | WMZTreeCustomCell *cell = [tableView dequeueReusableCellWithIdentifier:NSStringFromClass([WMZTreeCustomCell class])];
259 | if (!cell) {
260 | cell = [[WMZTreeCustomCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:NSStringFromClass([WMZTreeCustomCell class]) parentModel:self.param];
261 | }
262 | cell.delagete = self;
263 | cell.model = self.data[indexPath.row];
264 | return cell;
265 | }
266 |
267 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
268 | NSObject *param = self.data[indexPath.row];
269 | [self tapNodeAction:param indexRow:indexPath.row];
270 | }
271 |
272 | - (void)tapNodeAction:(NSObject*)param indexRow:(NSInteger)row{
273 | param.isExpand = !param.isExpand;
274 | if (param.children.count) {
275 | if (param.isExpand) {
276 | NSMutableArray *insetArr = [self getSonData:param type:TreeDataInsert];
277 | NSIndexSet *indexSet = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(row + 1, insetArr.count)];
278 | [self.data insertObjects:insetArr atIndexes:indexSet];
279 | }else{
280 | NSArray *arr = [self getSonData:param type:TreeDataDelete];
281 | [self.data removeObjectsInArray:arr];
282 | }
283 | }
284 | else{
285 | param.isExpand = NO;
286 | }
287 | /// 手风琴效果
288 | if (self.param.wAccordion&¶m.isExpand) {
289 | NSObject *parentModel = param.parentId?(self.dic[param.parentId]):self.tree;
290 | for (NSObject *model in parentModel.children) {
291 | if (!model.isExpand||model == param) continue;
292 | if (model.isExpand) {
293 | model.isExpand = NO;
294 | [self.data removeObjectsInArray:[self getSonData:model type:TreeDataDelete]];
295 | }
296 | }
297 | }
298 | /// 设计数据过多 全局刷新
299 | [self.table reloadData];
300 | if (self.param.wEventNodeClick) {
301 | self.param.wEventNodeClick(param);
302 | }
303 | }
304 |
305 | #pragma WMZtreeCellDelagete
306 | - (void)selectNode:(NSObject *)param checkStrictly:(BOOL)checkStrictly{
307 | [self setSelectNodeParentAndSonNodeStatus:param checkStrictly:checkStrictly reload:YES];
308 | if (self.param.wEventCheckChange) {
309 | self.param.wEventCheckChange(param,param.isSelected);
310 | }
311 | }
312 |
313 | - (void)userWithNode:(NSObject *)param param:(id)data cell:(id)cell{
314 | if (self.param.wEventCellUserEnabled) {
315 | self.param.wEventCellUserEnabled(param, [self.table indexPathForCell:cell], self.table,data);
316 | }
317 | }
318 |
319 | /// 关联所有父级和所有子级
320 | - (void)setSelectNodeParentAndSonNodeStatus:(NSObject *)param checkStrictly:(BOOL)checkStrictly reload:(BOOL)reload{
321 | /// 关闭多选
322 | if (!self.param.wCanMultipleSelect) {
323 | NSArray *sameLevel = [self getSonData:param type:TreeDataSameLevel];
324 | [sameLevel enumerateObjectsUsingBlock:^(NSObject* _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
325 | obj.isSelected = NO;
326 | obj.halfSelect = NO;
327 | }];
328 | if (reload) {
329 | [self.table reloadData];
330 | }
331 | }else{
332 | if (checkStrictly) {
333 | /// 关联上级
334 | NSArray *parentNode = nil;
335 | if (param.parentId) {
336 | parentNode = [self searchAllParentNode:param];
337 | }
338 |
339 | /// 关联下级
340 | if (param.children.count) {
341 | [self getSonData:param type:TreeDataSelectAll];
342 | }
343 |
344 | if (reload) {
345 | [self.table reloadData];
346 | }
347 | }else{
348 | //不关联直接刷新
349 | NSIndexPath *path = [NSIndexPath indexPathForRow:[self.data indexOfObject:param] inSection:0];
350 | [self.table beginUpdates];
351 | [self.table reloadRowsAtIndexPaths:@[path] withRowAnimation:UITableViewRowAnimationNone];
352 | [self.table endUpdates];
353 | }
354 | }
355 | }
356 |
357 | - (BOOL)updateKeyChildren:(NSString*)currrentID data:(NSArray*)data{
358 | if (!currrentID) {
359 | NSLog(@"节点id不能为空");return NO;
360 | }
361 |
362 | if (currrentID&&currrentID.length) {
363 | NSObject *currentParam = self.dic[currrentID];
364 |
365 | /// 删除旧的
366 | NSArray *arr = [self getSonData:currentParam type:999];
367 | for (NSObject *param in arr) {
368 | if ([self.data indexOfObject:param]!=NSNotFound) {
369 | [self.data removeObject:param];
370 | }
371 | [self.dic removeObjectForKey:param.currentId];
372 | }
373 |
374 | /// 替换新的
375 | currentParam.children = [NSMutableArray arrayWithArray:data];
376 | for (NSObject *param in data) {
377 | if (param) {
378 | [self.dic setObject:param forKey:param.currentId];
379 | }
380 | }
381 |
382 | if (currentParam.isExpand) {
383 | NSIndexPath *indexPath = [NSIndexPath indexPathForRow:[self.data indexOfObject:currentParam] inSection:0];
384 | NSArray *insetArr = [self getSonData:currentParam type:TreeDataInsert];
385 | NSIndexSet *indexSet = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(indexPath.row + 1, insetArr.count)];
386 | [self.data insertObjects:insetArr atIndexes:indexSet];
387 | }
388 |
389 | [self.table reloadData];
390 | }else{
391 | [self.dic removeAllObjects];
392 | [self.data removeAllObjects];
393 | self.param.wData = [NSArray arrayWithArray:data];
394 | [self setUp];
395 | }
396 |
397 | return YES;
398 | }
399 |
400 | - (NSArray*)getCheckedNodesWithHalfSelect:(BOOL)halfSelect{
401 | NSMutableArray *allData = [self getSonData:self.tree type:TreeDataGetSelectAll];
402 | NSMutableArray *checkArr = [NSMutableArray new];
403 | for (NSObject *param in allData) {
404 | if (param.isSelected&¶m.canSelect ) {
405 | if (param) [checkArr addObject:param];
406 | }else{
407 | if (halfSelect&¶m.halfSelect&¶m.canSelect) {
408 | if(param) [checkArr addObject:param];
409 | }
410 | }
411 | }
412 | return [NSArray arrayWithArray:checkArr];
413 | }
414 |
415 | - (BOOL)append:(NSString*)currrentID node:(NSObject*)param{
416 | BOOL success = NO;
417 | if (!currrentID) {
418 | NSLog(@"节点id不能为空");return NO;
419 | }
420 | if (!param||![param isKindOfClass:[NSObject class]]) {
421 | NSLog(@"子节点错误");return NO;
422 | }
423 | if ([self.dic objectForKey:param.currentId]) {
424 | NSLog(@"要添加的节点已存在");return NO;
425 | }
426 | NSObject *parent = self.dic[currrentID];
427 | param.depath = parent.depath+1;
428 | NSArray *parentExpandArr = [self getSonData:parent type:TreeDataInsert];
429 | if(param) [parent.children addObject:param];
430 | [self.dic setObject:param forKey:param.currentId];
431 | NSArray *arr = [self getSonData:param type:TreeDataAllWithSelf];
432 | NSInteger index = [self.data indexOfObject:parent];
433 | NSIndexSet *indexSet = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(index+ parentExpandArr.count + 1, arr.count)];
434 | [self.data insertObjects:arr atIndexes:indexSet];
435 |
436 | if (!parent.isExpand) {
437 | NSInteger num = [self.data indexOfObject:parent];
438 | if (num!=NSNotFound) {
439 | [self tapNodeAction:parent indexRow:num];
440 | }
441 | }
442 |
443 | [self.table reloadData];
444 |
445 | return success;
446 | }
447 |
448 | - (BOOL)remove:(NSString*)currrentID{
449 | if (!currrentID) {
450 | NSLog(@"节点id不能为空");return NO;
451 | }
452 | BOOL success = NO;
453 | NSObject *parent = self.dic[currrentID];
454 | if (!parent) {
455 | NSLog(@"节点不存在");return NO;
456 | }
457 | /// 删除自身
458 | if ([self.dic valueForKey:parent.currentId]) {
459 | [self.dic removeObjectForKey:parent.currentId];
460 | success = YES;
461 | }
462 | if ([self.data indexOfObject:parent]!=NSNotFound) {
463 | [self.data removeObject:parent];
464 | success = YES;
465 | }
466 | if (parent.parentId) {
467 | NSObject *parentNode = self.dic[parent.parentId];
468 | [parentNode.children removeObject:parent];
469 | }
470 |
471 | /// 删除子节点
472 | NSArray *parentExpandArr = [self getSonData:parent type:TreeDataDelete];
473 | for (NSObject *param in parentExpandArr) {
474 | if ([self.dic valueForKey:param.currentId]) {
475 | [self.dic removeObjectForKey:param.currentId];
476 | }
477 | if ([self.data indexOfObject:param]!=NSNotFound) {
478 | [self.data removeObject:param];
479 | }
480 | }
481 |
482 | [self.table reloadData];
483 |
484 | return success;
485 | }
486 |
487 | - (BOOL)insertBefore:(NSString*)currrentID node:(NSObject*)param{
488 | if (!currrentID) {
489 | NSLog(@"节点id不能为空");return NO;
490 | }
491 | if (!param||![param isKindOfClass:[NSObject class]]) {
492 | NSLog(@"追加的节点错误");return NO;
493 | }
494 | if ([self.dic objectForKey:param.currentId]) {
495 | NSLog(@"要添加的节点已存在");return NO;
496 | }
497 | BOOL success = NO;
498 | NSObject *node = self.dic[currrentID];
499 | param.depath = node.depath;
500 | [self.dic setObject:param forKey:param.currentId];
501 | NSInteger index = [self.data indexOfObject:node];
502 | if (!node.parentId) {
503 | NSInteger sonIndex= [self.tree.children indexOfObject:node];
504 | [self.tree.children insertObject:param atIndex:sonIndex];
505 | }else{
506 | NSObject *parentNode = self.dic[node.parentId];
507 | NSInteger sonIndex= [parentNode.children indexOfObject:node];
508 | [parentNode.children insertObject:param atIndex:sonIndex];
509 | }
510 |
511 | [self.data insertObject:param atIndex:index];
512 |
513 | if (param.isExpand) {
514 | NSArray *arr = [self getSonData:param type:999];
515 | NSIndexSet *indexSet = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(index + 1, arr.count)];
516 | [self.data insertObjects:arr atIndexes:indexSet];
517 |
518 | }
519 | [self.table reloadData];
520 | return success;
521 | }
522 |
523 | - (BOOL)insertAfter:(NSString*)currrentID node:(NSObject*)param{
524 | if (!currrentID) {
525 | NSLog(@"节点id不能为空");return NO;
526 | }
527 | if (!param||![param isKindOfClass:[NSObject class]]) {
528 | NSLog(@"追加的节点错误");return NO;
529 | }
530 | if ([self.dic objectForKey:param.currentId]) {
531 | NSLog(@"要添加的节点已存在");return NO;
532 | }
533 | BOOL success = NO;
534 | NSObject *node = self.dic[currrentID];
535 | param.depath = node.depath;
536 | [self.dic setObject:param forKey:param.currentId];
537 | NSArray *parentExpandArr = [self getSonData:node type:TreeDataInsert];
538 | NSInteger index = [self.data indexOfObject:node];
539 | if (!node.parentId) {
540 | if(param) [self.tree.children addObject:param];
541 | }else{
542 | NSObject *parentNode = self.dic[node.parentId];
543 | if(param) [parentNode.children addObject:param];
544 | }
545 |
546 | [self.data insertObject:param atIndex:index+1+parentExpandArr.count];
547 |
548 | if (param.isExpand) {
549 | NSArray *arr = [self getSonData:param type:999];
550 | NSIndexSet *indexSet = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(index + 2+ parentExpandArr.count, arr.count)];
551 | [self.data insertObjects:arr atIndexes:indexSet];
552 |
553 | }
554 | [self.table reloadData];
555 | return success;
556 | }
557 |
558 | - (NSObject*)getParentId:(NSString*)currrentID{
559 | NSObject *node = self.dic[currrentID];
560 | return self.dic[node.parentId];
561 | }
562 |
563 | - (void)update{
564 | [self.data removeAllObjects];
565 | [self setUp];
566 | [self.table reloadData];
567 | }
568 |
569 | @end
570 |
--------------------------------------------------------------------------------
/WMZTreeView.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 50;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | F315C067236178C0001E1584 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = F315C066236178C0001E1584 /* AppDelegate.m */; };
11 | F315C06A236178C0001E1584 /* SceneDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = F315C069236178C0001E1584 /* SceneDelegate.m */; };
12 | F315C06D236178C0001E1584 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = F315C06C236178C0001E1584 /* ViewController.m */; };
13 | F315C070236178C0001E1584 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F315C06E236178C0001E1584 /* Main.storyboard */; };
14 | F315C072236178C1001E1584 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F315C071236178C1001E1584 /* Assets.xcassets */; };
15 | F315C075236178C1001E1584 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F315C073236178C1001E1584 /* LaunchScreen.storyboard */; };
16 | F315C078236178C1001E1584 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = F315C077236178C1001E1584 /* main.m */; };
17 | F315C082236178C2001E1584 /* WMZTreeViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F315C081236178C2001E1584 /* WMZTreeViewTests.m */; };
18 | F315C08D236178C2001E1584 /* WMZTreeViewUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = F315C08C236178C2001E1584 /* WMZTreeViewUITests.m */; };
19 | F315C0AB2361791B001E1584 /* WMZTreeView.bundle in Resources */ = {isa = PBXBuildFile; fileRef = F315C09D2361791B001E1584 /* WMZTreeView.bundle */; };
20 | F315C0AC2361791B001E1584 /* WMZTreeCustomCell.m in Sources */ = {isa = PBXBuildFile; fileRef = F315C09E2361791B001E1584 /* WMZTreeCustomCell.m */; };
21 | F315C0AD2361791B001E1584 /* WMZTreeViewParam.m in Sources */ = {isa = PBXBuildFile; fileRef = F315C0A12361791B001E1584 /* WMZTreeViewParam.m */; };
22 | F315C0AE2361791B001E1584 /* WMZTreeView.m in Sources */ = {isa = PBXBuildFile; fileRef = F315C0A22361791B001E1584 /* WMZTreeView.m */; };
23 | F315C0AF2361791B001E1584 /* WMZTreeParam.m in Sources */ = {isa = PBXBuildFile; fileRef = F315C0A42361791B001E1584 /* WMZTreeParam.m */; };
24 | F315C0B02361791B001E1584 /* WMZTreeBaseView.m in Sources */ = {isa = PBXBuildFile; fileRef = F315C0A52361791B001E1584 /* WMZTreeBaseView.m */; };
25 | F315C0B12361791B001E1584 /* DemoVC.m in Sources */ = {isa = PBXBuildFile; fileRef = F315C0A92361791B001E1584 /* DemoVC.m */; };
26 | F315C0B22361791B001E1584 /* WMZMyCell.m in Sources */ = {isa = PBXBuildFile; fileRef = F315C0AA2361791B001E1584 /* WMZMyCell.m */; };
27 | F35B1D7728F68E81005978FC /* WMZCustomModel.m in Sources */ = {isa = PBXBuildFile; fileRef = F35B1D7628F68E81005978FC /* WMZCustomModel.m */; };
28 | /* End PBXBuildFile section */
29 |
30 | /* Begin PBXContainerItemProxy section */
31 | F315C07E236178C2001E1584 /* PBXContainerItemProxy */ = {
32 | isa = PBXContainerItemProxy;
33 | containerPortal = F315C05A236178C0001E1584 /* Project object */;
34 | proxyType = 1;
35 | remoteGlobalIDString = F315C061236178C0001E1584;
36 | remoteInfo = WMZTreeView;
37 | };
38 | F315C089236178C2001E1584 /* PBXContainerItemProxy */ = {
39 | isa = PBXContainerItemProxy;
40 | containerPortal = F315C05A236178C0001E1584 /* Project object */;
41 | proxyType = 1;
42 | remoteGlobalIDString = F315C061236178C0001E1584;
43 | remoteInfo = WMZTreeView;
44 | };
45 | /* End PBXContainerItemProxy section */
46 |
47 | /* Begin PBXFileReference section */
48 | F315C062236178C0001E1584 /* WMZTreeView.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = WMZTreeView.app; sourceTree = BUILT_PRODUCTS_DIR; };
49 | F315C065236178C0001E1584 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; };
50 | F315C066236178C0001E1584 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; };
51 | F315C068236178C0001E1584 /* SceneDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SceneDelegate.h; sourceTree = ""; };
52 | F315C069236178C0001E1584 /* SceneDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SceneDelegate.m; sourceTree = ""; };
53 | F315C06B236178C0001E1584 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; };
54 | F315C06C236178C0001E1584 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; };
55 | F315C06F236178C0001E1584 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
56 | F315C071236178C1001E1584 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
57 | F315C074236178C1001E1584 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
58 | F315C076236178C1001E1584 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
59 | F315C077236178C1001E1584 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; };
60 | F315C07D236178C2001E1584 /* WMZTreeViewTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = WMZTreeViewTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
61 | F315C081236178C2001E1584 /* WMZTreeViewTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = WMZTreeViewTests.m; sourceTree = ""; };
62 | F315C083236178C2001E1584 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
63 | F315C088236178C2001E1584 /* WMZTreeViewUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = WMZTreeViewUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
64 | F315C08C236178C2001E1584 /* WMZTreeViewUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = WMZTreeViewUITests.m; sourceTree = ""; };
65 | F315C08E236178C2001E1584 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
66 | F315C09B2361791B001E1584 /* WMZTreeConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WMZTreeConfig.h; sourceTree = ""; };
67 | F315C09C2361791B001E1584 /* WMZTreeView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WMZTreeView.h; sourceTree = ""; };
68 | F315C09D2361791B001E1584 /* WMZTreeView.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = WMZTreeView.bundle; sourceTree = ""; };
69 | F315C09E2361791B001E1584 /* WMZTreeCustomCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WMZTreeCustomCell.m; sourceTree = ""; };
70 | F315C09F2361791B001E1584 /* WMZTreeParam.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WMZTreeParam.h; sourceTree = ""; };
71 | F315C0A02361791B001E1584 /* WMZTreeBaseView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WMZTreeBaseView.h; sourceTree = ""; };
72 | F315C0A12361791B001E1584 /* WMZTreeViewParam.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WMZTreeViewParam.m; sourceTree = ""; };
73 | F315C0A22361791B001E1584 /* WMZTreeView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WMZTreeView.m; sourceTree = ""; };
74 | F315C0A32361791B001E1584 /* WMZTreeCustomCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WMZTreeCustomCell.h; sourceTree = ""; };
75 | F315C0A42361791B001E1584 /* WMZTreeParam.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WMZTreeParam.m; sourceTree = ""; };
76 | F315C0A52361791B001E1584 /* WMZTreeBaseView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WMZTreeBaseView.m; sourceTree = ""; };
77 | F315C0A62361791B001E1584 /* WMZTreeViewParam.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WMZTreeViewParam.h; sourceTree = ""; };
78 | F315C0A72361791B001E1584 /* WMZMyCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WMZMyCell.h; sourceTree = ""; };
79 | F315C0A82361791B001E1584 /* DemoVC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DemoVC.h; sourceTree = ""; };
80 | F315C0A92361791B001E1584 /* DemoVC.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DemoVC.m; sourceTree = ""; };
81 | F315C0AA2361791B001E1584 /* WMZMyCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WMZMyCell.m; sourceTree = ""; };
82 | F35B1D7428F64FA8005978FC /* WMZTreeProcotol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WMZTreeProcotol.h; sourceTree = ""; };
83 | F35B1D7528F68E81005978FC /* WMZCustomModel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WMZCustomModel.h; sourceTree = ""; };
84 | F35B1D7628F68E81005978FC /* WMZCustomModel.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = WMZCustomModel.m; sourceTree = ""; };
85 | /* End PBXFileReference section */
86 |
87 | /* Begin PBXFrameworksBuildPhase section */
88 | F315C05F236178C0001E1584 /* Frameworks */ = {
89 | isa = PBXFrameworksBuildPhase;
90 | buildActionMask = 2147483647;
91 | files = (
92 | );
93 | runOnlyForDeploymentPostprocessing = 0;
94 | };
95 | F315C07A236178C2001E1584 /* Frameworks */ = {
96 | isa = PBXFrameworksBuildPhase;
97 | buildActionMask = 2147483647;
98 | files = (
99 | );
100 | runOnlyForDeploymentPostprocessing = 0;
101 | };
102 | F315C085236178C2001E1584 /* Frameworks */ = {
103 | isa = PBXFrameworksBuildPhase;
104 | buildActionMask = 2147483647;
105 | files = (
106 | );
107 | runOnlyForDeploymentPostprocessing = 0;
108 | };
109 | /* End PBXFrameworksBuildPhase section */
110 |
111 | /* Begin PBXGroup section */
112 | F315C059236178C0001E1584 = {
113 | isa = PBXGroup;
114 | children = (
115 | F315C064236178C0001E1584 /* WMZTreeView */,
116 | F315C080236178C2001E1584 /* WMZTreeViewTests */,
117 | F315C08B236178C2001E1584 /* WMZTreeViewUITests */,
118 | F315C063236178C0001E1584 /* Products */,
119 | );
120 | sourceTree = "";
121 | };
122 | F315C063236178C0001E1584 /* Products */ = {
123 | isa = PBXGroup;
124 | children = (
125 | F315C062236178C0001E1584 /* WMZTreeView.app */,
126 | F315C07D236178C2001E1584 /* WMZTreeViewTests.xctest */,
127 | F315C088236178C2001E1584 /* WMZTreeViewUITests.xctest */,
128 | );
129 | name = Products;
130 | sourceTree = "";
131 | };
132 | F315C064236178C0001E1584 /* WMZTreeView */ = {
133 | isa = PBXGroup;
134 | children = (
135 | F315C09A2361791B001E1584 /* WMZTreeView */,
136 | F315C065236178C0001E1584 /* AppDelegate.h */,
137 | F315C066236178C0001E1584 /* AppDelegate.m */,
138 | F315C068236178C0001E1584 /* SceneDelegate.h */,
139 | F315C069236178C0001E1584 /* SceneDelegate.m */,
140 | F315C0A82361791B001E1584 /* DemoVC.h */,
141 | F315C0A92361791B001E1584 /* DemoVC.m */,
142 | F315C0A72361791B001E1584 /* WMZMyCell.h */,
143 | F315C0AA2361791B001E1584 /* WMZMyCell.m */,
144 | F315C06B236178C0001E1584 /* ViewController.h */,
145 | F315C06C236178C0001E1584 /* ViewController.m */,
146 | F315C06E236178C0001E1584 /* Main.storyboard */,
147 | F315C071236178C1001E1584 /* Assets.xcassets */,
148 | F315C073236178C1001E1584 /* LaunchScreen.storyboard */,
149 | F315C076236178C1001E1584 /* Info.plist */,
150 | F315C077236178C1001E1584 /* main.m */,
151 | F35B1D7528F68E81005978FC /* WMZCustomModel.h */,
152 | F35B1D7628F68E81005978FC /* WMZCustomModel.m */,
153 | );
154 | path = WMZTreeView;
155 | sourceTree = "";
156 | };
157 | F315C080236178C2001E1584 /* WMZTreeViewTests */ = {
158 | isa = PBXGroup;
159 | children = (
160 | F315C081236178C2001E1584 /* WMZTreeViewTests.m */,
161 | F315C083236178C2001E1584 /* Info.plist */,
162 | );
163 | path = WMZTreeViewTests;
164 | sourceTree = "";
165 | };
166 | F315C08B236178C2001E1584 /* WMZTreeViewUITests */ = {
167 | isa = PBXGroup;
168 | children = (
169 | F315C08C236178C2001E1584 /* WMZTreeViewUITests.m */,
170 | F315C08E236178C2001E1584 /* Info.plist */,
171 | );
172 | path = WMZTreeViewUITests;
173 | sourceTree = "";
174 | };
175 | F315C09A2361791B001E1584 /* WMZTreeView */ = {
176 | isa = PBXGroup;
177 | children = (
178 | F315C09C2361791B001E1584 /* WMZTreeView.h */,
179 | F315C0A22361791B001E1584 /* WMZTreeView.m */,
180 | F315C09B2361791B001E1584 /* WMZTreeConfig.h */,
181 | F315C0A32361791B001E1584 /* WMZTreeCustomCell.h */,
182 | F315C09E2361791B001E1584 /* WMZTreeCustomCell.m */,
183 | F315C09F2361791B001E1584 /* WMZTreeParam.h */,
184 | F315C0A42361791B001E1584 /* WMZTreeParam.m */,
185 | F315C0A02361791B001E1584 /* WMZTreeBaseView.h */,
186 | F315C0A52361791B001E1584 /* WMZTreeBaseView.m */,
187 | F315C0A62361791B001E1584 /* WMZTreeViewParam.h */,
188 | F315C0A12361791B001E1584 /* WMZTreeViewParam.m */,
189 | F35B1D7428F64FA8005978FC /* WMZTreeProcotol.h */,
190 | F315C09D2361791B001E1584 /* WMZTreeView.bundle */,
191 | );
192 | path = WMZTreeView;
193 | sourceTree = "";
194 | };
195 | /* End PBXGroup section */
196 |
197 | /* Begin PBXNativeTarget section */
198 | F315C061236178C0001E1584 /* WMZTreeView */ = {
199 | isa = PBXNativeTarget;
200 | buildConfigurationList = F315C091236178C2001E1584 /* Build configuration list for PBXNativeTarget "WMZTreeView" */;
201 | buildPhases = (
202 | F315C05E236178C0001E1584 /* Sources */,
203 | F315C05F236178C0001E1584 /* Frameworks */,
204 | F315C060236178C0001E1584 /* Resources */,
205 | );
206 | buildRules = (
207 | );
208 | dependencies = (
209 | );
210 | name = WMZTreeView;
211 | productName = WMZTreeView;
212 | productReference = F315C062236178C0001E1584 /* WMZTreeView.app */;
213 | productType = "com.apple.product-type.application";
214 | };
215 | F315C07C236178C2001E1584 /* WMZTreeViewTests */ = {
216 | isa = PBXNativeTarget;
217 | buildConfigurationList = F315C094236178C2001E1584 /* Build configuration list for PBXNativeTarget "WMZTreeViewTests" */;
218 | buildPhases = (
219 | F315C079236178C2001E1584 /* Sources */,
220 | F315C07A236178C2001E1584 /* Frameworks */,
221 | F315C07B236178C2001E1584 /* Resources */,
222 | );
223 | buildRules = (
224 | );
225 | dependencies = (
226 | F315C07F236178C2001E1584 /* PBXTargetDependency */,
227 | );
228 | name = WMZTreeViewTests;
229 | productName = WMZTreeViewTests;
230 | productReference = F315C07D236178C2001E1584 /* WMZTreeViewTests.xctest */;
231 | productType = "com.apple.product-type.bundle.unit-test";
232 | };
233 | F315C087236178C2001E1584 /* WMZTreeViewUITests */ = {
234 | isa = PBXNativeTarget;
235 | buildConfigurationList = F315C097236178C2001E1584 /* Build configuration list for PBXNativeTarget "WMZTreeViewUITests" */;
236 | buildPhases = (
237 | F315C084236178C2001E1584 /* Sources */,
238 | F315C085236178C2001E1584 /* Frameworks */,
239 | F315C086236178C2001E1584 /* Resources */,
240 | );
241 | buildRules = (
242 | );
243 | dependencies = (
244 | F315C08A236178C2001E1584 /* PBXTargetDependency */,
245 | );
246 | name = WMZTreeViewUITests;
247 | productName = WMZTreeViewUITests;
248 | productReference = F315C088236178C2001E1584 /* WMZTreeViewUITests.xctest */;
249 | productType = "com.apple.product-type.bundle.ui-testing";
250 | };
251 | /* End PBXNativeTarget section */
252 |
253 | /* Begin PBXProject section */
254 | F315C05A236178C0001E1584 /* Project object */ = {
255 | isa = PBXProject;
256 | attributes = {
257 | LastUpgradeCheck = 1100;
258 | ORGANIZATIONNAME = wmz;
259 | TargetAttributes = {
260 | F315C061236178C0001E1584 = {
261 | CreatedOnToolsVersion = 11.0;
262 | };
263 | F315C07C236178C2001E1584 = {
264 | CreatedOnToolsVersion = 11.0;
265 | TestTargetID = F315C061236178C0001E1584;
266 | };
267 | F315C087236178C2001E1584 = {
268 | CreatedOnToolsVersion = 11.0;
269 | TestTargetID = F315C061236178C0001E1584;
270 | };
271 | };
272 | };
273 | buildConfigurationList = F315C05D236178C0001E1584 /* Build configuration list for PBXProject "WMZTreeView" */;
274 | compatibilityVersion = "Xcode 9.3";
275 | developmentRegion = en;
276 | hasScannedForEncodings = 0;
277 | knownRegions = (
278 | en,
279 | Base,
280 | );
281 | mainGroup = F315C059236178C0001E1584;
282 | productRefGroup = F315C063236178C0001E1584 /* Products */;
283 | projectDirPath = "";
284 | projectRoot = "";
285 | targets = (
286 | F315C061236178C0001E1584 /* WMZTreeView */,
287 | F315C07C236178C2001E1584 /* WMZTreeViewTests */,
288 | F315C087236178C2001E1584 /* WMZTreeViewUITests */,
289 | );
290 | };
291 | /* End PBXProject section */
292 |
293 | /* Begin PBXResourcesBuildPhase section */
294 | F315C060236178C0001E1584 /* Resources */ = {
295 | isa = PBXResourcesBuildPhase;
296 | buildActionMask = 2147483647;
297 | files = (
298 | F315C075236178C1001E1584 /* LaunchScreen.storyboard in Resources */,
299 | F315C072236178C1001E1584 /* Assets.xcassets in Resources */,
300 | F315C070236178C0001E1584 /* Main.storyboard in Resources */,
301 | F315C0AB2361791B001E1584 /* WMZTreeView.bundle in Resources */,
302 | );
303 | runOnlyForDeploymentPostprocessing = 0;
304 | };
305 | F315C07B236178C2001E1584 /* Resources */ = {
306 | isa = PBXResourcesBuildPhase;
307 | buildActionMask = 2147483647;
308 | files = (
309 | );
310 | runOnlyForDeploymentPostprocessing = 0;
311 | };
312 | F315C086236178C2001E1584 /* Resources */ = {
313 | isa = PBXResourcesBuildPhase;
314 | buildActionMask = 2147483647;
315 | files = (
316 | );
317 | runOnlyForDeploymentPostprocessing = 0;
318 | };
319 | /* End PBXResourcesBuildPhase section */
320 |
321 | /* Begin PBXSourcesBuildPhase section */
322 | F315C05E236178C0001E1584 /* Sources */ = {
323 | isa = PBXSourcesBuildPhase;
324 | buildActionMask = 2147483647;
325 | files = (
326 | F315C0AF2361791B001E1584 /* WMZTreeParam.m in Sources */,
327 | F315C0AC2361791B001E1584 /* WMZTreeCustomCell.m in Sources */,
328 | F315C0B02361791B001E1584 /* WMZTreeBaseView.m in Sources */,
329 | F315C0B12361791B001E1584 /* DemoVC.m in Sources */,
330 | F315C06D236178C0001E1584 /* ViewController.m in Sources */,
331 | F35B1D7728F68E81005978FC /* WMZCustomModel.m in Sources */,
332 | F315C067236178C0001E1584 /* AppDelegate.m in Sources */,
333 | F315C0AD2361791B001E1584 /* WMZTreeViewParam.m in Sources */,
334 | F315C078236178C1001E1584 /* main.m in Sources */,
335 | F315C0AE2361791B001E1584 /* WMZTreeView.m in Sources */,
336 | F315C0B22361791B001E1584 /* WMZMyCell.m in Sources */,
337 | F315C06A236178C0001E1584 /* SceneDelegate.m in Sources */,
338 | );
339 | runOnlyForDeploymentPostprocessing = 0;
340 | };
341 | F315C079236178C2001E1584 /* Sources */ = {
342 | isa = PBXSourcesBuildPhase;
343 | buildActionMask = 2147483647;
344 | files = (
345 | F315C082236178C2001E1584 /* WMZTreeViewTests.m in Sources */,
346 | );
347 | runOnlyForDeploymentPostprocessing = 0;
348 | };
349 | F315C084236178C2001E1584 /* Sources */ = {
350 | isa = PBXSourcesBuildPhase;
351 | buildActionMask = 2147483647;
352 | files = (
353 | F315C08D236178C2001E1584 /* WMZTreeViewUITests.m in Sources */,
354 | );
355 | runOnlyForDeploymentPostprocessing = 0;
356 | };
357 | /* End PBXSourcesBuildPhase section */
358 |
359 | /* Begin PBXTargetDependency section */
360 | F315C07F236178C2001E1584 /* PBXTargetDependency */ = {
361 | isa = PBXTargetDependency;
362 | target = F315C061236178C0001E1584 /* WMZTreeView */;
363 | targetProxy = F315C07E236178C2001E1584 /* PBXContainerItemProxy */;
364 | };
365 | F315C08A236178C2001E1584 /* PBXTargetDependency */ = {
366 | isa = PBXTargetDependency;
367 | target = F315C061236178C0001E1584 /* WMZTreeView */;
368 | targetProxy = F315C089236178C2001E1584 /* PBXContainerItemProxy */;
369 | };
370 | /* End PBXTargetDependency section */
371 |
372 | /* Begin PBXVariantGroup section */
373 | F315C06E236178C0001E1584 /* Main.storyboard */ = {
374 | isa = PBXVariantGroup;
375 | children = (
376 | F315C06F236178C0001E1584 /* Base */,
377 | );
378 | name = Main.storyboard;
379 | sourceTree = "";
380 | };
381 | F315C073236178C1001E1584 /* LaunchScreen.storyboard */ = {
382 | isa = PBXVariantGroup;
383 | children = (
384 | F315C074236178C1001E1584 /* Base */,
385 | );
386 | name = LaunchScreen.storyboard;
387 | sourceTree = "";
388 | };
389 | /* End PBXVariantGroup section */
390 |
391 | /* Begin XCBuildConfiguration section */
392 | F315C08F236178C2001E1584 /* Debug */ = {
393 | isa = XCBuildConfiguration;
394 | buildSettings = {
395 | ALWAYS_SEARCH_USER_PATHS = NO;
396 | CLANG_ANALYZER_NONNULL = YES;
397 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
398 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
399 | CLANG_CXX_LIBRARY = "libc++";
400 | CLANG_ENABLE_MODULES = YES;
401 | CLANG_ENABLE_OBJC_ARC = YES;
402 | CLANG_ENABLE_OBJC_WEAK = YES;
403 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
404 | CLANG_WARN_BOOL_CONVERSION = YES;
405 | CLANG_WARN_COMMA = YES;
406 | CLANG_WARN_CONSTANT_CONVERSION = YES;
407 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
408 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
409 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
410 | CLANG_WARN_EMPTY_BODY = YES;
411 | CLANG_WARN_ENUM_CONVERSION = YES;
412 | CLANG_WARN_INFINITE_RECURSION = YES;
413 | CLANG_WARN_INT_CONVERSION = YES;
414 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
415 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
416 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
417 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
418 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
419 | CLANG_WARN_STRICT_PROTOTYPES = YES;
420 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
421 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
422 | CLANG_WARN_UNREACHABLE_CODE = YES;
423 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
424 | COPY_PHASE_STRIP = NO;
425 | DEBUG_INFORMATION_FORMAT = dwarf;
426 | ENABLE_STRICT_OBJC_MSGSEND = YES;
427 | ENABLE_TESTABILITY = YES;
428 | GCC_C_LANGUAGE_STANDARD = gnu11;
429 | GCC_DYNAMIC_NO_PIC = NO;
430 | GCC_NO_COMMON_BLOCKS = YES;
431 | GCC_OPTIMIZATION_LEVEL = 0;
432 | GCC_PREPROCESSOR_DEFINITIONS = (
433 | "DEBUG=1",
434 | "$(inherited)",
435 | );
436 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
437 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
438 | GCC_WARN_UNDECLARED_SELECTOR = YES;
439 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
440 | GCC_WARN_UNUSED_FUNCTION = YES;
441 | GCC_WARN_UNUSED_VARIABLE = YES;
442 | IPHONEOS_DEPLOYMENT_TARGET = 13.0;
443 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
444 | MTL_FAST_MATH = YES;
445 | ONLY_ACTIVE_ARCH = YES;
446 | SDKROOT = iphoneos;
447 | };
448 | name = Debug;
449 | };
450 | F315C090236178C2001E1584 /* Release */ = {
451 | isa = XCBuildConfiguration;
452 | buildSettings = {
453 | ALWAYS_SEARCH_USER_PATHS = NO;
454 | CLANG_ANALYZER_NONNULL = YES;
455 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
456 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
457 | CLANG_CXX_LIBRARY = "libc++";
458 | CLANG_ENABLE_MODULES = YES;
459 | CLANG_ENABLE_OBJC_ARC = YES;
460 | CLANG_ENABLE_OBJC_WEAK = YES;
461 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
462 | CLANG_WARN_BOOL_CONVERSION = YES;
463 | CLANG_WARN_COMMA = YES;
464 | CLANG_WARN_CONSTANT_CONVERSION = YES;
465 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
466 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
467 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
468 | CLANG_WARN_EMPTY_BODY = YES;
469 | CLANG_WARN_ENUM_CONVERSION = YES;
470 | CLANG_WARN_INFINITE_RECURSION = YES;
471 | CLANG_WARN_INT_CONVERSION = YES;
472 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
473 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
474 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
475 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
476 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
477 | CLANG_WARN_STRICT_PROTOTYPES = YES;
478 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
479 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
480 | CLANG_WARN_UNREACHABLE_CODE = YES;
481 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
482 | COPY_PHASE_STRIP = NO;
483 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
484 | ENABLE_NS_ASSERTIONS = NO;
485 | ENABLE_STRICT_OBJC_MSGSEND = YES;
486 | GCC_C_LANGUAGE_STANDARD = gnu11;
487 | GCC_NO_COMMON_BLOCKS = YES;
488 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
489 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
490 | GCC_WARN_UNDECLARED_SELECTOR = YES;
491 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
492 | GCC_WARN_UNUSED_FUNCTION = YES;
493 | GCC_WARN_UNUSED_VARIABLE = YES;
494 | IPHONEOS_DEPLOYMENT_TARGET = 13.0;
495 | MTL_ENABLE_DEBUG_INFO = NO;
496 | MTL_FAST_MATH = YES;
497 | SDKROOT = iphoneos;
498 | VALIDATE_PRODUCT = YES;
499 | };
500 | name = Release;
501 | };
502 | F315C092236178C2001E1584 /* Debug */ = {
503 | isa = XCBuildConfiguration;
504 | buildSettings = {
505 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
506 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
507 | CODE_SIGN_STYLE = Automatic;
508 | DEVELOPMENT_TEAM = HR6T4LQD9T;
509 | INFOPLIST_FILE = WMZTreeView/Info.plist;
510 | LD_RUNPATH_SEARCH_PATHS = (
511 | "$(inherited)",
512 | "@executable_path/Frameworks",
513 | );
514 | PRODUCT_BUNDLE_IDENTIFIER = com.WMZTreeView;
515 | PRODUCT_NAME = "$(TARGET_NAME)";
516 | TARGETED_DEVICE_FAMILY = "1,2";
517 | };
518 | name = Debug;
519 | };
520 | F315C093236178C2001E1584 /* Release */ = {
521 | isa = XCBuildConfiguration;
522 | buildSettings = {
523 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
524 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
525 | CODE_SIGN_STYLE = Automatic;
526 | DEVELOPMENT_TEAM = HR6T4LQD9T;
527 | INFOPLIST_FILE = WMZTreeView/Info.plist;
528 | LD_RUNPATH_SEARCH_PATHS = (
529 | "$(inherited)",
530 | "@executable_path/Frameworks",
531 | );
532 | PRODUCT_BUNDLE_IDENTIFIER = com.WMZTreeView;
533 | PRODUCT_NAME = "$(TARGET_NAME)";
534 | TARGETED_DEVICE_FAMILY = "1,2";
535 | };
536 | name = Release;
537 | };
538 | F315C095236178C2001E1584 /* Debug */ = {
539 | isa = XCBuildConfiguration;
540 | buildSettings = {
541 | BUNDLE_LOADER = "$(TEST_HOST)";
542 | CODE_SIGN_STYLE = Automatic;
543 | DEVELOPMENT_TEAM = HR6T4LQD9T;
544 | INFOPLIST_FILE = WMZTreeViewTests/Info.plist;
545 | IPHONEOS_DEPLOYMENT_TARGET = 13.0;
546 | LD_RUNPATH_SEARCH_PATHS = (
547 | "$(inherited)",
548 | "@executable_path/Frameworks",
549 | "@loader_path/Frameworks",
550 | );
551 | PRODUCT_BUNDLE_IDENTIFIER = com.WMZTreeViewTests;
552 | PRODUCT_NAME = "$(TARGET_NAME)";
553 | TARGETED_DEVICE_FAMILY = "1,2";
554 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/WMZTreeView.app/WMZTreeView";
555 | };
556 | name = Debug;
557 | };
558 | F315C096236178C2001E1584 /* Release */ = {
559 | isa = XCBuildConfiguration;
560 | buildSettings = {
561 | BUNDLE_LOADER = "$(TEST_HOST)";
562 | CODE_SIGN_STYLE = Automatic;
563 | DEVELOPMENT_TEAM = HR6T4LQD9T;
564 | INFOPLIST_FILE = WMZTreeViewTests/Info.plist;
565 | IPHONEOS_DEPLOYMENT_TARGET = 13.0;
566 | LD_RUNPATH_SEARCH_PATHS = (
567 | "$(inherited)",
568 | "@executable_path/Frameworks",
569 | "@loader_path/Frameworks",
570 | );
571 | PRODUCT_BUNDLE_IDENTIFIER = com.WMZTreeViewTests;
572 | PRODUCT_NAME = "$(TARGET_NAME)";
573 | TARGETED_DEVICE_FAMILY = "1,2";
574 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/WMZTreeView.app/WMZTreeView";
575 | };
576 | name = Release;
577 | };
578 | F315C098236178C2001E1584 /* Debug */ = {
579 | isa = XCBuildConfiguration;
580 | buildSettings = {
581 | CODE_SIGN_STYLE = Automatic;
582 | DEVELOPMENT_TEAM = HR6T4LQD9T;
583 | INFOPLIST_FILE = WMZTreeViewUITests/Info.plist;
584 | LD_RUNPATH_SEARCH_PATHS = (
585 | "$(inherited)",
586 | "@executable_path/Frameworks",
587 | "@loader_path/Frameworks",
588 | );
589 | PRODUCT_BUNDLE_IDENTIFIER = com.WMZTreeViewUITests;
590 | PRODUCT_NAME = "$(TARGET_NAME)";
591 | TARGETED_DEVICE_FAMILY = "1,2";
592 | TEST_TARGET_NAME = WMZTreeView;
593 | };
594 | name = Debug;
595 | };
596 | F315C099236178C2001E1584 /* Release */ = {
597 | isa = XCBuildConfiguration;
598 | buildSettings = {
599 | CODE_SIGN_STYLE = Automatic;
600 | DEVELOPMENT_TEAM = HR6T4LQD9T;
601 | INFOPLIST_FILE = WMZTreeViewUITests/Info.plist;
602 | LD_RUNPATH_SEARCH_PATHS = (
603 | "$(inherited)",
604 | "@executable_path/Frameworks",
605 | "@loader_path/Frameworks",
606 | );
607 | PRODUCT_BUNDLE_IDENTIFIER = com.WMZTreeViewUITests;
608 | PRODUCT_NAME = "$(TARGET_NAME)";
609 | TARGETED_DEVICE_FAMILY = "1,2";
610 | TEST_TARGET_NAME = WMZTreeView;
611 | };
612 | name = Release;
613 | };
614 | /* End XCBuildConfiguration section */
615 |
616 | /* Begin XCConfigurationList section */
617 | F315C05D236178C0001E1584 /* Build configuration list for PBXProject "WMZTreeView" */ = {
618 | isa = XCConfigurationList;
619 | buildConfigurations = (
620 | F315C08F236178C2001E1584 /* Debug */,
621 | F315C090236178C2001E1584 /* Release */,
622 | );
623 | defaultConfigurationIsVisible = 0;
624 | defaultConfigurationName = Release;
625 | };
626 | F315C091236178C2001E1584 /* Build configuration list for PBXNativeTarget "WMZTreeView" */ = {
627 | isa = XCConfigurationList;
628 | buildConfigurations = (
629 | F315C092236178C2001E1584 /* Debug */,
630 | F315C093236178C2001E1584 /* Release */,
631 | );
632 | defaultConfigurationIsVisible = 0;
633 | defaultConfigurationName = Release;
634 | };
635 | F315C094236178C2001E1584 /* Build configuration list for PBXNativeTarget "WMZTreeViewTests" */ = {
636 | isa = XCConfigurationList;
637 | buildConfigurations = (
638 | F315C095236178C2001E1584 /* Debug */,
639 | F315C096236178C2001E1584 /* Release */,
640 | );
641 | defaultConfigurationIsVisible = 0;
642 | defaultConfigurationName = Release;
643 | };
644 | F315C097236178C2001E1584 /* Build configuration list for PBXNativeTarget "WMZTreeViewUITests" */ = {
645 | isa = XCConfigurationList;
646 | buildConfigurations = (
647 | F315C098236178C2001E1584 /* Debug */,
648 | F315C099236178C2001E1584 /* Release */,
649 | );
650 | defaultConfigurationIsVisible = 0;
651 | defaultConfigurationName = Release;
652 | };
653 | /* End XCConfigurationList section */
654 | };
655 | rootObject = F315C05A236178C0001E1584 /* Project object */;
656 | }
657 |
--------------------------------------------------------------------------------