├── LICENSE ├── README.md ├── WMZTreeView.podspec ├── WMZTreeView.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcshareddata │ │ └── IDEWorkspaceChecks.plist │ └── xcuserdata │ │ └── wmz.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ └── wmz.xcuserdatad │ ├── xcdebugger │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ └── xcschememanagement.plist ├── WMZTreeView ├── AppDelegate.h ├── AppDelegate.m ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ ├── Contents.json │ └── default_maintenance.imageset │ │ ├── Contents.json │ │ └── default_maintenance@3x.png ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── DemoVC.h ├── DemoVC.m ├── Info.plist ├── SceneDelegate.h ├── SceneDelegate.m ├── ViewController.h ├── ViewController.m ├── WMZCustomModel.h ├── WMZCustomModel.m ├── WMZMyCell.h ├── WMZMyCell.m ├── WMZTreeView │ ├── WMZTreeBaseView.h │ ├── WMZTreeBaseView.m │ ├── WMZTreeConfig.h │ ├── WMZTreeCustomCell.h │ ├── WMZTreeCustomCell.m │ ├── WMZTreeParam.h │ ├── WMZTreeParam.m │ ├── WMZTreeProcotol.h │ ├── WMZTreeView.bundle │ │ ├── close.png │ │ ├── open.png │ │ ├── treeCheck.png │ │ ├── treeCheck1.png │ │ ├── treeCheckSelect.png │ │ └── treeHalfSelect.png │ ├── WMZTreeView.h │ ├── WMZTreeView.m │ ├── WMZTreeViewParam.h │ └── WMZTreeViewParam.m └── main.m ├── WMZTreeViewTests ├── Info.plist └── WMZTreeViewTests.m └── WMZTreeViewUITests ├── Info.plist └── WMZTreeViewUITests.m /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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## UI效果仿照前端element-UI的[Tree控件](https://element.eleme.cn/#/zh-CN/component/tree) 2 | [![Platform](https://img.shields.io/badge/platform-iOS-red.svg)](https://developer.apple.com/iphone/index.action) 3 | [![CocoaPods Compatible](https://img.shields.io/cocoapods/v/WMZTreeView.svg)](https://img.shields.io/cocoapods/v/WMZTreeView.svg) 4 | [![License](https://img.shields.io/badge/license-MIT-blue.svg)](http://mit-license.org) 5 | 6 | # **视图层** 7 | 8 | ### 正常树形显示 9 | 10 | 这里显示十级 每级100条数据 总共1000条数据的效果图 11 | 12 | ![treeNone.gif](https://upload-images.jianshu.io/upload_images/9163368-4c0890c33838370a.gif?imageMogr2/auto-orient/strip) 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 | ![treeSelect.gif](https://upload-images.jianshu.io/upload_images/9163368-22a48815e35c5df9.gif?imageMogr2/auto-orient/strip) 22 | 23 | ``` 24 | TreeViewParam() 25 | //可勾选 26 | .wShowCheckboxSet(YES) 27 | //节点字体高亮颜色 28 | .wHighlightCurrentSet(TreeColor(0x1d76db)) 29 | ``` 30 | 31 | ### 自定义节点内容+增删节点 32 | 33 | ![treeCell.gif](https://upload-images.jianshu.io/upload_images/9163368-c2b322014e6d2121.gif?imageMogr2/auto-orient/strip) 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 | ![treeCanSelect.gif](https://upload-images.jianshu.io/upload_images/9163368-607e5c23e4c77b1e.gif?imageMogr2/auto-orient/strip) 87 | 88 | 89 | ``` 90 | //手风琴效果 同级只展开一级 91 | TreeViewParam().wAccordionSet(YES) 92 | 93 | //能否选择 94 | TreeParam().canSelectSet(NO) 95 | ``` 96 | ### 勾选不关联父节点和子节点+默认选中+默认全部展开 97 | 98 | ![treeCheck.gif](https://upload-images.jianshu.io/upload_images/9163368-c651d0107797fc8c.gif?imageMogr2/auto-orient/strip) 99 | 100 | ``` 101 | TreeViewParam() 102 | //父节点和子节点 勾选不关联 103 | .wCheckStrictlySet(NO) 104 | //默认展开全部 105 | .wDefaultExpandAllSet(YES) 106 | //默认勾选 107 | .wDefaultExpandedKeysSet(@[@"5",@"10",@"12"]) 108 | 109 | ``` 110 | ### 开启拖拽 111 | ![treeDraggable.gif](https://upload-images.jianshu.io/upload_images/9163368-5c6a589140e7078a.gif?imageMogr2/auto-orient/strip) 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.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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /WMZTreeView.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /WMZTreeView.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /WMZTreeView.xcodeproj/project.xcworkspace/xcuserdata/wmz.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwmz/WMZTreeView/fffd9f4064136a48449edf8900ec767d5d1ab7a6/WMZTreeView.xcodeproj/project.xcworkspace/xcuserdata/wmz.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /WMZTreeView.xcodeproj/xcuserdata/wmz.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 9 | 17 | 18 | 19 | 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/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/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/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /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/Assets.xcassets/default_maintenance.imageset/default_maintenance@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwmz/WMZTreeView/fffd9f4064136a48449edf8900ec767d5d1ab7a6/WMZTreeView/Assets.xcassets/default_maintenance.imageset/default_maintenance@3x.png -------------------------------------------------------------------------------- /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/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/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/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/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/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/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/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/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/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/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/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/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/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 | -------------------------------------------------------------------------------- /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/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/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/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 | -------------------------------------------------------------------------------- /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/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/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/WMZTreeView.bundle/close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwmz/WMZTreeView/fffd9f4064136a48449edf8900ec767d5d1ab7a6/WMZTreeView/WMZTreeView/WMZTreeView.bundle/close.png -------------------------------------------------------------------------------- /WMZTreeView/WMZTreeView/WMZTreeView.bundle/open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwmz/WMZTreeView/fffd9f4064136a48449edf8900ec767d5d1ab7a6/WMZTreeView/WMZTreeView/WMZTreeView.bundle/open.png -------------------------------------------------------------------------------- /WMZTreeView/WMZTreeView/WMZTreeView.bundle/treeCheck.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwmz/WMZTreeView/fffd9f4064136a48449edf8900ec767d5d1ab7a6/WMZTreeView/WMZTreeView/WMZTreeView.bundle/treeCheck.png -------------------------------------------------------------------------------- /WMZTreeView/WMZTreeView/WMZTreeView.bundle/treeCheck1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwmz/WMZTreeView/fffd9f4064136a48449edf8900ec767d5d1ab7a6/WMZTreeView/WMZTreeView/WMZTreeView.bundle/treeCheck1.png -------------------------------------------------------------------------------- /WMZTreeView/WMZTreeView/WMZTreeView.bundle/treeCheckSelect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwmz/WMZTreeView/fffd9f4064136a48449edf8900ec767d5d1ab7a6/WMZTreeView/WMZTreeView/WMZTreeView.bundle/treeCheckSelect.png -------------------------------------------------------------------------------- /WMZTreeView/WMZTreeView/WMZTreeView.bundle/treeHalfSelect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwmz/WMZTreeView/fffd9f4064136a48449edf8900ec767d5d1ab7a6/WMZTreeView/WMZTreeView/WMZTreeView.bundle/treeHalfSelect.png -------------------------------------------------------------------------------- /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/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/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/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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | --------------------------------------------------------------------------------