├── .gitignore ├── CHD_ListView_Structure.podspec ├── CHD_ListView_Structure ├── CHD_ListView_Structure.h └── CHD_ListView_Structure.m ├── LICENSE ├── README.md └── ScreenShots ├── BSBDJ ├── IMG_1663.PNG ├── IMG_1664.PNG ├── IMG_1665.PNG └── IMG_1666.PNG └── WYYYD ├── IMG_1667.PNG ├── IMG_1668.PNG ├── IMG_1669.PNG └── IMG_1670.PNG /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xccheckout 23 | *.xcscmblueprint 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | *.ipa 28 | *.dSYM.zip 29 | *.dSYM 30 | 31 | # CocoaPods 32 | # 33 | # We recommend against adding the Pods directory to your .gitignore. However 34 | # you should judge for yourself, the pros and cons are mentioned at: 35 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 36 | # 37 | # Pods/ 38 | 39 | # Carthage 40 | # 41 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 42 | # Carthage/Checkouts 43 | 44 | Carthage/Build 45 | 46 | # fastlane 47 | # 48 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 49 | # screenshots whenever they are needed. 50 | # For more information about the recommended setup visit: 51 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 52 | 53 | fastlane/report.xml 54 | fastlane/Preview.html 55 | fastlane/screenshots 56 | fastlane/test_output 57 | 58 | # Code Injection 59 | # 60 | # After new code Injection tools there's a generated folder /iOSInjectionProject 61 | # https://github.com/johnno1962/injectionforxcode 62 | 63 | iOSInjectionProject/ -------------------------------------------------------------------------------- /CHD_ListView_Structure.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Be sure to run `pod spec lint chd_listview_structure.podspec' to ensure this is a 3 | # valid spec and to remove all comments including this before submitting the spec. 4 | # 5 | # To learn more about Podspec attributes see http://docs.cocoapods.org/specification.html 6 | # To see working Podspecs in the CocoaPods repo see https://github.com/CocoaPods/Specs/ 7 | # 8 | 9 | Pod::Spec.new do |s| 10 | 11 | # ――― Spec Metadata ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 12 | # 13 | # These will help people to find your library, and whilst it 14 | # can feel like a chore to fill in it's definitely to your advantage. The 15 | # summary should be tweet-length, and the description more in depth. 16 | # 17 | 18 | s.name = "CHD_ListView_Structure" 19 | s.version = "1.0.5" 20 | s.summary = "show the structure of UITableView and UICollectionView (展示tableView和collectionView的结构)" 21 | 22 | # This description is used to generate tags and improve search results. 23 | # * Think: What does it do? Why did you write it? What is the focus? 24 | # * Try to keep it short, snappy and to the point. 25 | # * Write the description between the DESC delimiters below. 26 | # * Finally, don't worry about the indent, CocoaPods strips it! 27 | # s.description = <<-DESC 28 | # DESC 29 | 30 | s.homepage = "https://github.com/donggelaile/CHD_ListView_Structure" 31 | # s.screenshots = "www.example.com/screenshots_1.gif", "www.example.com/screenshots_2.gif" 32 | 33 | 34 | # ――― Spec License ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 35 | # 36 | # Licensing your code is important. See http://choosealicense.com for more info. 37 | # CocoaPods will detect a license file if there is a named LICENSE* 38 | # Popular ones are 'MIT', 'BSD' and 'Apache License, Version 2.0'. 39 | # 40 | 41 | s.license = "MIT" 42 | # s.license = { :type => "MIT", :file => "FILE_LICENSE" } 43 | 44 | 45 | # ――― Author Metadata ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 46 | # 47 | # Specify the authors of the library, with email addresses. Email addresses 48 | # of the authors are extracted from the SCM log. E.g. $ git log. CocoaPods also 49 | # accepts just a name if you'd rather not provide an email address. 50 | # 51 | # Specify a social_media_url where others can refer to, for example a twitter 52 | # profile URL. 53 | # 54 | 55 | s.author = { "donggelaile" => "519623144@qq.com" } 56 | # Or just: s.author = "donggelaile" 57 | # s.authors = { "donggelaile" => "519623144@qq.com" } 58 | # s.social_media_url = "http://twitter.com/donggelaile" 59 | 60 | # ――― Platform Specifics ――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 61 | # 62 | # If this Pod runs only on iOS or OS X, then specify the platform and 63 | # the deployment target. You can optionally include the target after the platform. 64 | # 65 | 66 | # s.platform = :ios 67 | s.platform = :ios, "6.0" 68 | 69 | # When using multiple platforms 70 | # s.ios.deployment_target = "5.0" 71 | # s.osx.deployment_target = "10.7" 72 | # s.watchos.deployment_target = "2.0" 73 | # s.tvos.deployment_target = "9.0" 74 | 75 | 76 | # ――― Source Location ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 77 | # 78 | # Specify the location from where the source should be retrieved. 79 | # Supports git, hg, bzr, svn and HTTP. 80 | # 81 | 82 | s.source = { :git => "https://github.com/donggelaile/CHD_ListView_Structure.git", :tag => "#{s.version}" } 83 | 84 | 85 | # ――― Source Code ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 86 | # 87 | # CocoaPods is smart about how it includes source code. For source files 88 | # giving a folder will include any swift, h, m, mm, c & cpp files. 89 | # For header files it will include any header in the folder. 90 | # Not including the public_header_files will make all headers public. 91 | # 92 | 93 | s.source_files = "CHD_ListView_Structure", "CHD_ListView_Structure/**/*.{h,m}" 94 | # s.exclude_files = "Classes/Exclude" 95 | 96 | # s.public_header_files = "Classes/**/*.h" 97 | 98 | 99 | # ――― Resources ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 100 | # 101 | # A list of resources included with the Pod. These are copied into the 102 | # target bundle with a build phase script. Anything else will be cleaned. 103 | # You can preserve files from being cleaned, please don't preserve 104 | # non-essential files like tests, examples and documentation. 105 | # 106 | 107 | # s.resource = "icon.png" 108 | # s.resources = "Resources/*.png" 109 | 110 | # s.preserve_paths = "FilesToSave", "MoreFilesToSave" 111 | 112 | 113 | # ――― Project Linking ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 114 | # 115 | # Link your library with frameworks, or libraries. Libraries do not include 116 | # the lib prefix of their name. 117 | # 118 | 119 | # s.framework = "SomeFramework" 120 | # s.frameworks = "SomeFramework", "AnotherFramework" 121 | 122 | # s.library = "iconv" 123 | # s.libraries = "iconv", "xml2" 124 | 125 | 126 | # ――― Project Settings ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 127 | # 128 | # If your library depends on compiler flags you can set them in the xcconfig hash 129 | # where they will only apply to your library. If you depend on other Podspecs 130 | # you can include multiple dependencies to ensure it works. 131 | 132 | # s.requires_arc = true 133 | 134 | # s.xcconfig = { "HEADER_SEARCH_PATHS" => "$(SDKROOT)/usr/include/libxml2" } 135 | # s.dependency "JSONKit", "~> 1.4" 136 | 137 | end 138 | -------------------------------------------------------------------------------- /CHD_ListView_Structure/CHD_ListView_Structure.h: -------------------------------------------------------------------------------- 1 | // 2 | // UITableView+Structure.h 3 | 4 | // 5 | // Created by chd on 2017/8/24. 6 | // Copyright © 2017年 chd. All rights reserved. 7 | // https://github.com/donggelaile/CHD_ListView_Structure 8 | 9 | #import 10 | 11 | //switch 12 | @interface CHD_ListView_Structure : NSObject 13 | /** 14 | 功能开关 15 | 16 | @param isOpenT 是否开启TableV结构展示 (ShowOrNot TableView Strcuture) 17 | @param isOpenC 是否开启colletionV结构展示 (ShowOrNot CollectionView Strcuture) 18 | 19 | */ 20 | + (void)openStructureShow_TableV:(BOOL)isOpenT collectionV:(BOOL)isOpenC;//只有开启了选项,Toggle才会生效。 21 | @end 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /CHD_ListView_Structure/CHD_ListView_Structure.m: -------------------------------------------------------------------------------- 1 | // 2 | // UITableView+Structure.m 3 | // 4 | // 5 | // Created by chd on 2017/8/24. 6 | // Copyright © 2017年 chd. All rights reserved. 7 | // https://github.com/donggelaile/CHD_ListView_Structure 8 | 9 | #import "CHD_ListView_Structure.h" 10 | #import 11 | 12 | 13 | //Switch 14 | @interface CHD_SwitchView : UIButton 15 | 16 | @end 17 | 18 | //HookHelper 19 | @interface CHD_HookHelper : NSObject 20 | @property (nonatomic, assign) BOOL is_open_chdTable; 21 | @property (nonatomic, assign) BOOL is_open_chdCollection; 22 | @property (nonatomic, retain) NSMapTable *weakListViewDic; 23 | + (instancetype)shareInstance; 24 | - (void)hookSelectors:(NSArray *)selArr orginalObj:(id)oriObj swizzedObj:(id)newObj; 25 | - (void)resetCHD_HoverView; 26 | @end 27 | 28 | 29 | //MustrHelper 30 | @interface CHD_MustrHelper : NSObject 31 | @end 32 | 33 | 34 | //Hover 35 | @interface CHD_HoverLabel : UILabel 36 | @property (nonatomic, retain)UIColor *borderColor; 37 | @end 38 | 39 | @interface UIView (CHD_HoverView) 40 | @end 41 | 42 | 43 | 44 | //UITableView 45 | @interface UITableViewHDStructure:NSObject 46 | @end 47 | 48 | @interface CHD_TableHelper : NSObject 49 | @end 50 | 51 | 52 | 53 | //UICollectionView 54 | @interface UICollectionViewHDStructure:NSObject 55 | @end 56 | 57 | @interface CHD_CollectionHelper : NSObject 58 | @end 59 | 60 | 61 | 62 | 63 | 64 | #define chd_table_head_view_color [UIColor magentaColor] 65 | #define chd_table_cell_color [UIColor redColor] 66 | #define chd_table_header_color [UIColor blueColor] 67 | #define chd_table_footer_color [UIColor greenColor] 68 | #define chd_text_bg_alpha 0.7 69 | #define chd_table_text_color [UIColor whiteColor] 70 | #define chd_table_footer_view_color [UIColor blackColor] 71 | 72 | 73 | #define chd_collection_cell_color [UIColor orangeColor] 74 | #define chd_collection_header_color [UIColor purpleColor] 75 | #define chd_collection_footer_color [UIColor cyanColor] 76 | #define chd_collection_decoration_color [UIColor blackColor] 77 | #define chd_collection_bg_alpha 1 78 | #define chd_collection_text_color [UIColor whiteColor] 79 | 80 | 81 | static NSString *const CHD_MapTable_Obj = @"CHD_MapTable_Obj"; 82 | 83 | 84 | BOOL __CHD_Instance_Transition_Swizzle(Class originalClass,SEL originalSelector, Class swizzledClass, SEL swizzledSelector){ 85 | #ifdef DEBUG 86 | 87 | Method originalMethod = class_getInstanceMethod(originalClass, originalSelector); 88 | Method swizzledMethod = class_getInstanceMethod(swizzledClass, swizzledSelector); 89 | 90 | if (!originalMethod) { 91 | //如果原对象原方法未实现,查看交换类是否帮其实现了原类方法 92 | Method tempM = class_getInstanceMethod(swizzledClass, originalSelector); 93 | if (tempM) { 94 | //给原对象增加原方法 95 | class_addMethod(originalClass, originalSelector, method_getImplementation(tempM), method_getTypeEncoding(tempM)); 96 | //更新原对象实现 97 | originalMethod = class_getInstanceMethod(originalClass, originalSelector); 98 | } 99 | } 100 | 101 | if (!originalMethod || !swizzledMethod) { 102 | return NO; 103 | } 104 | 105 | IMP originalIMP = method_getImplementation(originalMethod); 106 | IMP swizzledIMP = method_getImplementation(swizzledMethod); 107 | const char *originalType = method_getTypeEncoding(originalMethod); 108 | const char *swizzledType = method_getTypeEncoding(swizzledMethod); 109 | //给原对象增加swizzledSelector方法,实现为originalIMP 110 | class_replaceMethod(originalClass,swizzledSelector,originalIMP,originalType); 111 | //替换originalSelector的实现为swizzledIMP 112 | class_replaceMethod(originalClass,originalSelector,swizzledIMP,swizzledType); 113 | return YES; 114 | #else 115 | return NO; 116 | #endif 117 | 118 | } 119 | 120 | @implementation CHD_SwitchView 121 | - (instancetype)initWithFrame:(CGRect)frame 122 | { 123 | if ([super initWithFrame:frame]) { 124 | UIPanGestureRecognizer* pan = [[UIPanGestureRecognizer alloc]initWithTarget:self action:@selector(handelPan:)]; 125 | [self addGestureRecognizer:pan]; 126 | 127 | self.alpha = 1; 128 | } 129 | return self; 130 | } 131 | - (void)handelPan:(UIPanGestureRecognizer*)gestureRecognizer 132 | { 133 | if (gestureRecognizer.state == UIGestureRecognizerStateBegan) { 134 | [UIView animateWithDuration:0.3 animations:^{ 135 | gestureRecognizer.view.alpha = 1; 136 | }]; 137 | } 138 | if (gestureRecognizer.state == UIGestureRecognizerStateEnded) { 139 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 140 | [UIView animateWithDuration:1 animations:^{ 141 | gestureRecognizer.view.alpha = 0.5; 142 | }]; 143 | }); 144 | } 145 | CGPoint center = [gestureRecognizer locationInView:self.superview]; 146 | 147 | CGFloat minX = CGRectGetWidth(self.frame)/2.0; 148 | CGFloat maxX = [UIScreen mainScreen].bounds.size.width - minX; 149 | CGFloat minY = CGRectGetHeight(self.frame)/2.0; 150 | CGFloat maxY = [UIScreen mainScreen].bounds.size.height - minY; 151 | 152 | if (center.xmaxX) { 159 | center = CGPointMake(maxX, center.y); 160 | } 161 | if (center.y>maxY) { 162 | center = CGPointMake(center.x, maxY); 163 | } 164 | 165 | self.center = center; 166 | } 167 | 168 | 169 | @end 170 | 171 | 172 | 173 | #pragma mark - CHD_ListView_Structure 174 | 175 | @implementation CHD_ListView_Structure 176 | 177 | +(void)openStructureShow_TableV:(BOOL)isOpenT collectionV:(BOOL)isOpenC 178 | { 179 | #ifdef DEBUG 180 | static dispatch_once_t onceToken; 181 | dispatch_once(&onceToken, ^{ 182 | [CHD_HookHelper shareInstance].is_open_chdTable = isOpenT; 183 | [CHD_HookHelper shareInstance].is_open_chdCollection = isOpenC; 184 | 185 | [CHD_ListView_Structure hookTable]; 186 | [CHD_ListView_Structure hookCollection]; 187 | 188 | [CHD_ListView_Structure addToggleView]; 189 | }); 190 | #endif 191 | } 192 | + (void)addToggleView 193 | { 194 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 195 | UIWindow *window = [[[UIApplication sharedApplication] delegate] window]; 196 | CGFloat btnW = 50.0f; 197 | CHD_SwitchView *btn = [[CHD_SwitchView alloc] initWithFrame:CGRectMake(0, 50, btnW, btnW)]; 198 | btn.alpha = 0.5; 199 | [btn setTitle:@"Toggle" forState:UIControlStateNormal]; 200 | btn.layer.cornerRadius = btnW/2.0f; 201 | btn.backgroundColor = [UIColor orangeColor]; 202 | btn.titleLabel.font = [UIFont systemFontOfSize:14.0f]; 203 | 204 | UITapGestureRecognizer* tap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(tapGesture)]; 205 | [btn addGestureRecognizer:tap]; 206 | [window addSubview:btn]; 207 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 208 | [window bringSubviewToFront:btn]; 209 | }); 210 | }); 211 | } 212 | + (void)hookTable 213 | { 214 | #pragma clang diagnostic push 215 | #pragma clang diagnostic ignored "-Wundeclared-selector" 216 | __CHD_Instance_Transition_Swizzle([UITableView class], @selector(setDelegate:), [UITableViewHDStructure class],@selector(CHD_setDelegate:)); 217 | __CHD_Instance_Transition_Swizzle([UITableView class], @selector(setDataSource:), [UITableViewHDStructure class],@selector(CHD_setDataSource:)); 218 | NSArray *selArr = @[@"setTableFooterView:",@"setTableHeaderView:"]; 219 | [[CHD_HookHelper shareInstance] hookSelectors:selArr orginalObj:[UITableView new] swizzedObj:[CHD_TableHelper class]]; 220 | #pragma clang diagnostic pop 221 | 222 | } 223 | + (void)hookCollection 224 | { 225 | #pragma clang diagnostic push 226 | #pragma clang diagnostic ignored "-Wundeclared-selector" 227 | __CHD_Instance_Transition_Swizzle([UICollectionView class], @selector(setDelegate:), [UICollectionViewHDStructure class], @selector(CHD_setDelegate:)); 228 | __CHD_Instance_Transition_Swizzle([UICollectionView class], @selector(setDataSource:), [UICollectionViewHDStructure class], @selector(CHD_setDataSource:)); 229 | #pragma clang diagnostic pop 230 | } 231 | 232 | + (void)tapGesture 233 | { 234 | [CHD_HookHelper shareInstance].is_open_chdTable = ![CHD_HookHelper shareInstance].is_open_chdTable; 235 | [CHD_HookHelper shareInstance].is_open_chdCollection = ![CHD_HookHelper shareInstance].is_open_chdCollection; 236 | [[CHD_HookHelper shareInstance] resetCHD_HoverView]; 237 | } 238 | 239 | @end 240 | 241 | 242 | #pragma mark - CHD_HookHelper 243 | 244 | static CHD_HookHelper *helper; 245 | 246 | @implementation CHD_HookHelper 247 | { 248 | NSMutableDictionary *swizzedData; 249 | } 250 | + (instancetype)shareInstance 251 | { 252 | UIApplication *app = [UIApplication sharedApplication]; 253 | 254 | helper = objc_getAssociatedObject(app, (__bridge const void * _Nonnull)(@(123))); 255 | if (!helper) { 256 | helper = [[self alloc] init]; 257 | objc_setAssociatedObject(app, (__bridge const void * _Nonnull)(@(123)), helper, OBJC_ASSOCIATION_RETAIN); 258 | } 259 | 260 | return helper; 261 | } 262 | 263 | 264 | - (instancetype)init 265 | { 266 | if (self = [super init]) { 267 | swizzedData = @{}.mutableCopy; 268 | self.weakListViewDic = [NSMapTable mapTableWithKeyOptions:NSPointerFunctionsWeakMemory valueOptions:NSPointerFunctionsCopyIn]; 269 | self.is_open_chdTable = NO; 270 | self.is_open_chdCollection = NO; 271 | } 272 | return self; 273 | } 274 | 275 | 276 | - (BOOL)chenckIsSwizzedOrgObj:(id)oriObj sel:(NSString*)sel 277 | { 278 | if (!oriObj ||!sel) { 279 | return YES; 280 | } 281 | BOOL isFatherChanged = NO;//查找是否有父类或同类已经交换过 282 | for (NSString *keys in [swizzedData allKeys]) { 283 | NSString *saveClass = [[keys componentsSeparatedByString:@"_chd_hook_"] firstObject]; 284 | if ([oriObj isKindOfClass:NSClassFromString(saveClass)] && [swizzedData[[self getUniqueStr:saveClass sel:sel]] boolValue]) { 285 | isFatherChanged = YES; 286 | break; 287 | } 288 | } 289 | if (isFatherChanged) { 290 | /* 291 | 1、父类已经交换过 292 | 2、这里未对子类再进行交换,原因是如果子类重载了方法并且调用了[super someMethod]将会递归循环 293 | 3、目前可以判断子类是否重载了某个函数,但无法判断子类内部是否调用了[super someMethod],所以目前先不对子类做处理 294 | 4、未对子类进行处理的情况下,如果子类调用了[super someMethod]或未重载父类方法将会正常显示,否则子类的页面将无法显示结构 295 | */ 296 | 297 | //综上有两种建议:1、不使用继承实现delegate和dataSource的方法 2、使用了继承在子类重载的话要调用[super someMethod] 298 | return isFatherChanged; 299 | } 300 | 301 | return [swizzedData[[self getUniqueStr:NSStringFromClass([oriObj class]) sel:sel]] boolValue]; 302 | } 303 | - (void)hookSelectors:(NSArray *)selArr orginalObj:(id)oriObj swizzedObj:(id)newObj 304 | { 305 | 306 | for (NSString *selStr in selArr) { 307 | SEL sel = NSSelectorFromString(selStr); 308 | SEL newSel = NSSelectorFromString([@"CHD_" stringByAppendingString:selStr]); 309 | if (![self chenckIsSwizzedOrgObj:oriObj sel:selStr]) { 310 | BOOL isSuccess = __CHD_Instance_Transition_Swizzle([oriObj class], sel, [newObj class], newSel); 311 | if (isSuccess) { 312 | swizzedData[[self getUniqueStr:NSStringFromClass([oriObj class]) sel:selStr]] = @(YES); 313 | } 314 | } 315 | 316 | // [oriObj aspect_hookSelector:sel withOptions:AspectPositionInstead usingBlock:^(id aspectInfo) { 317 | // 318 | // 319 | // } error:nil]; 320 | } 321 | 322 | 323 | } 324 | 325 | - (nullable NSString *)getUniqueStr:(NSString*)oriObjClass sel:(NSString*)sel 326 | { 327 | if (!oriObjClass||!sel) { 328 | return nil; 329 | } 330 | return [NSString stringWithFormat:@"%@_chd_hook_%@",oriObjClass,sel]; 331 | } 332 | 333 | 334 | - (void)resetCHD_HoverView 335 | { 336 | NSArray *WeakListViewArr = [[[CHD_HookHelper shareInstance].weakListViewDic keyEnumerator] allObjects]; 337 | NSLog(@"当前listView个数:%@",@(WeakListViewArr.count)); 338 | for (UIView *listView in WeakListViewArr) { 339 | if ([listView isKindOfClass:[UITableView class]] || [listView isKindOfClass:[UICollectionView class]]) { 340 | 341 | //刷新当前listView 342 | [listView performSelectorOnMainThread:@selector(reloadData) withObject:nil waitUntilDone:NO]; 343 | 344 | //刷新tableHederView、tableFooterView 345 | if ([listView isKindOfClass:[UITableView class]]) { 346 | UIView *tabelHeader = [(UITableView *)listView tableHeaderView]; 347 | if (tabelHeader) { 348 | [(UITableView *)listView setTableHeaderView:tabelHeader]; 349 | } 350 | UIView *tableFooter = [(UITableView *)listView tableFooterView]; 351 | if (tableFooter) { 352 | [(UITableView *)listView setTableFooterView:tableFooter]; 353 | } 354 | } 355 | } 356 | } 357 | 358 | } 359 | 360 | 361 | @end 362 | 363 | 364 | #pragma mark - CHD_MustrHelper 365 | @implementation CHD_MustrHelper 366 | 367 | + (NSMutableAttributedString *)getMustr:(NSString*)str textColor:(UIColor *)textColor backGroundColor:(UIColor *)backColor 368 | { 369 | NSMutableAttributedString *Mstr = [[NSMutableAttributedString alloc] initWithString:str]; 370 | [Mstr addAttribute:NSBackgroundColorAttributeName value:backColor range:NSMakeRange(0, str.length)]; 371 | [Mstr addAttribute:NSForegroundColorAttributeName value:textColor range:NSMakeRange(0, str.length)]; 372 | return Mstr; 373 | } 374 | 375 | @end 376 | 377 | 378 | 379 | 380 | #pragma mark - UIView(CHD_HoverView) 381 | static const void * CHDHOVERLABELKEY = "CHDHOVERLABELKEY"; 382 | @implementation UIView(CHD_HoverView) 383 | 384 | - (UILabel *)hoverView:(UIColor*)borderColor 385 | { 386 | 387 | CHD_HoverLabel *hover = objc_getAssociatedObject(self, CHDHOVERLABELKEY); 388 | 389 | if (!hover) { 390 | hover = [[CHD_HoverLabel alloc] initWithFrame:self.bounds]; 391 | hover.backgroundColor = [UIColor clearColor]; 392 | hover.textAlignment = NSTextAlignmentCenter; 393 | hover.adjustsFontSizeToFitWidth = YES; 394 | [self addSubview:hover]; 395 | 396 | hover.translatesAutoresizingMaskIntoConstraints = YES; 397 | hover.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; 398 | hover.borderColor = borderColor; 399 | objc_setAssociatedObject(self, CHDHOVERLABELKEY, hover, OBJC_ASSOCIATION_RETAIN); 400 | return hover; 401 | }else{ 402 | [self bringSubviewToFront:hover]; 403 | hover.borderColor = borderColor; 404 | return hover; 405 | } 406 | 407 | } 408 | @end 409 | 410 | @implementation CHD_HoverLabel 411 | +(Class)layerClass 412 | { 413 | return [CAShapeLayer class]; 414 | } 415 | - (void)layoutSubviews 416 | { 417 | [super layoutSubviews]; 418 | CAShapeLayer *layer = (CAShapeLayer *)self.layer; 419 | layer.frame = self.bounds; 420 | layer.path = [UIBezierPath bezierPathWithRect:CGRectInset(self.bounds, 1, 1)].CGPath;//增加一个小的间隙,这样多个cell的边缘不会重合 421 | layer.fillColor = [UIColor clearColor].CGColor; 422 | layer.strokeColor = self.borderColor.CGColor; 423 | layer.lineWidth = 1; 424 | 425 | } 426 | @end 427 | 428 | 429 | #pragma mark - UITableView (CHD_Structure) 430 | @implementation UITableViewHDStructure 431 | - (void)CHD_setDelegate:(id)delegate 432 | { 433 | if (delegate) { 434 | NSMutableArray *selArr = @[@"tableView:willDisplayFooterView:forSection:",@"tableView:willDisplayHeaderView:forSection:"].mutableCopy; 435 | 436 | [[CHD_HookHelper shareInstance] hookSelectors:selArr orginalObj:delegate swizzedObj:[CHD_TableHelper class]]; 437 | [[CHD_HookHelper shareInstance].weakListViewDic setObject:CHD_MapTable_Obj forKey:self]; 438 | } 439 | [self CHD_setDelegate:delegate]; 440 | 441 | } 442 | - (void)CHD_setDataSource:(id)dataSource 443 | { 444 | if (dataSource) { 445 | NSArray *selArr = @[@"tableView:cellForRowAtIndexPath:"]; 446 | 447 | [[CHD_HookHelper shareInstance] hookSelectors:selArr orginalObj:dataSource swizzedObj:[CHD_TableHelper class]]; 448 | [[CHD_HookHelper shareInstance].weakListViewDic setObject:CHD_MapTable_Obj forKey:self]; 449 | } 450 | [self CHD_setDataSource:dataSource]; 451 | 452 | } 453 | 454 | @end 455 | 456 | @implementation CHD_TableHelper 457 | 458 | - (void)CHD_setTableHeaderView:(UIView *)tableHeaderView 459 | { 460 | UILabel *headerHover = [tableHeaderView hoverView:chd_table_head_view_color]; 461 | headerHover.attributedText = [CHD_MustrHelper getMustr:[NSString stringWithFormat:@"HeaderView--%@",NSStringFromClass([tableHeaderView class])] textColor:chd_table_text_color backGroundColor:chd_table_head_view_color]; 462 | [self CHD_setTableHeaderView:tableHeaderView]; 463 | headerHover.hidden = ![CHD_HookHelper shareInstance].is_open_chdTable; 464 | } 465 | - (void)CHD_setTableFooterView:(UIView *)tableFooterView 466 | { 467 | UILabel *footerHover = [tableFooterView hoverView:chd_table_footer_view_color]; 468 | footerHover.attributedText = [CHD_MustrHelper getMustr:[NSString stringWithFormat:@"FooterView--%@",NSStringFromClass([tableFooterView class])] textColor:chd_table_text_color backGroundColor:chd_table_footer_view_color]; 469 | [self CHD_setTableFooterView:tableFooterView]; 470 | footerHover.hidden = ![CHD_HookHelper shareInstance].is_open_chdTable; 471 | } 472 | 473 | 474 | -(UITableViewCell *)CHD_tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 475 | { 476 | UITableViewCell *cell = [self CHD_tableView:tableView cellForRowAtIndexPath:indexPath]; 477 | UILabel *hover = [cell hoverView:chd_table_cell_color]; 478 | hover.attributedText = [CHD_MustrHelper getMustr:[NSString stringWithFormat:@"%@--%@--%@",NSStringFromClass([cell class]),@(indexPath.section),@(indexPath.row)] textColor:chd_table_text_color backGroundColor:[chd_table_cell_color colorWithAlphaComponent:chd_text_bg_alpha]]; 479 | hover.hidden = ![CHD_HookHelper shareInstance].is_open_chdTable; 480 | return cell; 481 | } 482 | 483 | 484 | //原代理未实现的话,主动添加一个空实现用来交换。实现了则直接交换 485 | - (void)tableView:(UITableView *)tableView willDisplayHeaderView:(nonnull UIView *)view forSection:(NSInteger)section 486 | { 487 | } 488 | - (void)tableView:(UITableView *)tableView willDisplayFooterView:(nonnull UIView *)view forSection:(NSInteger)section 489 | { 490 | } 491 | - (void)CHD_tableView:(UITableView *)tableView willDisplayHeaderView:(nonnull UIView *)view forSection:(NSInteger)section 492 | { 493 | [self CHD_tableView:tableView willDisplayHeaderView:view forSection:section]; 494 | 495 | if ([view isKindOfClass:[UILabel class]]) { 496 | [view layoutIfNeeded]; 497 | } 498 | UILabel *hover = [view hoverView:chd_table_header_color]; 499 | hover.attributedText = [CHD_MustrHelper getMustr:[NSString stringWithFormat:@"Header--%@--%@",NSStringFromClass([view class]),@(section)] textColor:chd_table_text_color backGroundColor:[chd_table_header_color colorWithAlphaComponent:chd_text_bg_alpha]]; 500 | hover.hidden = ![CHD_HookHelper shareInstance].is_open_chdTable; 501 | 502 | } 503 | - (void)CHD_tableView:(UITableView *)tableView willDisplayFooterView:(nonnull UIView *)view forSection:(NSInteger)section 504 | { 505 | [self CHD_tableView:tableView willDisplayFooterView:view forSection:section]; 506 | 507 | if ([view isKindOfClass:[UILabel class]]) { 508 | [view layoutIfNeeded]; 509 | } 510 | UILabel *hover = [view hoverView:chd_table_footer_color]; 511 | hover.attributedText = [CHD_MustrHelper getMustr:[NSString stringWithFormat:@"Footer--%@--%@",NSStringFromClass([view class]),@(section)] textColor:chd_table_text_color backGroundColor:[chd_table_footer_color colorWithAlphaComponent:chd_text_bg_alpha]]; 512 | hover.hidden = ![CHD_HookHelper shareInstance].is_open_chdTable; 513 | } 514 | 515 | @end 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 | 526 | //CollectionView 527 | #pragma mark - UICollectionView (CHD_Structure) 528 | 529 | @implementation UICollectionViewHDStructure 530 | 531 | - (void)CHD_setDelegate:(id)delegate 532 | { 533 | if (delegate) { 534 | NSArray *selArr; 535 | if ([UIDevice currentDevice].systemVersion.floatValue>=8.0) { 536 | selArr = @[@"collectionView:willDisplaySupplementaryView:forElementKind:atIndexPath:"]; 537 | }else{ 538 | selArr = @[@"collectionView:didEndDisplayingSupplementaryView:forElementOfKind:atIndexPath:"]; 539 | } 540 | 541 | 542 | [[CHD_HookHelper shareInstance] hookSelectors:selArr orginalObj:delegate swizzedObj:[CHD_CollectionHelper class]]; 543 | [[CHD_HookHelper shareInstance].weakListViewDic setObject:CHD_MapTable_Obj forKey:self]; 544 | 545 | } 546 | [self CHD_setDelegate:delegate]; 547 | } 548 | - (void)CHD_setDataSource:(id)dataSource 549 | { 550 | if (dataSource) { 551 | NSArray *selArr = @[@"collectionView:cellForItemAtIndexPath:"]; 552 | 553 | [[CHD_HookHelper shareInstance] hookSelectors:selArr orginalObj:dataSource swizzedObj:[CHD_CollectionHelper class]]; 554 | [[CHD_HookHelper shareInstance].weakListViewDic setObject:CHD_MapTable_Obj forKey:self]; 555 | 556 | } 557 | [self CHD_setDataSource:dataSource]; 558 | } 559 | 560 | @end 561 | 562 | @implementation CHD_CollectionHelper 563 | 564 | + (NSAttributedString*)hoverAtt:(NSIndexPath*)indexPath cell:(UICollectionViewCell*)cell cacheToObj:(NSObject*)target 565 | { 566 | static char *HDListViewHoverTextCacheKey; 567 | NSMutableDictionary *hoverCache = objc_getAssociatedObject(target, &HDListViewHoverTextCacheKey); 568 | if (!hoverCache) { 569 | hoverCache = @{}.mutableCopy; 570 | objc_setAssociatedObject(target, &HDListViewHoverTextCacheKey, hoverCache, OBJC_ASSOCIATION_RETAIN); 571 | } 572 | NSString *key = [NSString stringWithFormat:@"%zd-%zd",indexPath.section,indexPath.item]; 573 | NSAttributedString *result = hoverCache[key]; 574 | if (!result) { 575 | result = [CHD_MustrHelper getMustr:[NSString stringWithFormat:@"%@++%@++%@",NSStringFromClass([cell class]),@(indexPath.section),@(indexPath.item)] textColor:chd_collection_text_color backGroundColor:[chd_collection_cell_color colorWithAlphaComponent:chd_collection_bg_alpha]]; 576 | hoverCache[key] = result; 577 | } 578 | return result; 579 | } 580 | - (void)someMthond 581 | { 582 | 583 | } 584 | - (__kindof UICollectionViewCell *)CHD_collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath 585 | { 586 | static BOOL isSHowTime = NO; 587 | __block UICollectionViewCell *cell = nil; 588 | 589 | 590 | if (isSHowTime) { 591 | //dispatch_benchmark 为系统私有API,不建议线上使用 592 | 593 | // //方法交换后需要注意一个问题,此处的代码中的self代表的是 UICollectionView的delegate 594 | // //此处的self不是CHD_CollectionHelper。因此 此处调用[self someMthond]; 编译不报错,运行时会报找不到方法 595 | // uint64_t dispatch_benchmark(size_t count, void (^block)(void)); 596 | // uint64_t ns = dispatch_benchmark(1, ^{ 597 | // cell = [self CHD_collectionView:collectionView cellForItemAtIndexPath:indexPath]; 598 | // }); 599 | // 600 | // double currentTime = ns/(pow(10, 6)); 601 | //#ifdef DEBUG 602 | // printf("cellForItemAtIndexPath----本次时间%lf \n",currentTime); 603 | //#endif 604 | // 605 | // static char *HDListViewCountKey; 606 | // static char *HDListViewScrollTotalTimeKey; 607 | // 608 | // NSNumber *numCount = objc_getAssociatedObject(self, &HDListViewCountKey); 609 | // NSInteger count = 1; 610 | // if (numCount) { 611 | // count = numCount.integerValue; 612 | // } 613 | // double totalTime = 0; 614 | // NSNumber *numTotal = objc_getAssociatedObject(self, &HDListViewScrollTotalTimeKey); 615 | // if (numCount) { 616 | // totalTime = numTotal.doubleValue; 617 | // } 618 | // totalTime += currentTime; 619 | // double eveTime = (totalTime)/count; 620 | //#ifdef DEBUG 621 | // printf("cellForItemAtIndexPath----平均时间%lf \n",eveTime); 622 | //#endif 623 | // count++; 624 | // 625 | // objc_setAssociatedObject(self, &HDListViewCountKey, @(count), OBJC_ASSOCIATION_RETAIN); 626 | // objc_setAssociatedObject(self, &HDListViewScrollTotalTimeKey, @(totalTime), OBJC_ASSOCIATION_RETAIN); 627 | }else{ 628 | cell = [self CHD_collectionView:collectionView cellForItemAtIndexPath:indexPath]; 629 | } 630 | 631 | 632 | UILabel *hover = [cell hoverView:chd_collection_cell_color]; 633 | hover.attributedText = [CHD_CollectionHelper hoverAtt:indexPath cell:cell cacheToObj:self]; 634 | hover.hidden = ![CHD_HookHelper shareInstance].is_open_chdCollection; 635 | 636 | return cell; 637 | } 638 | 639 | //如果原代理未实现如下方法会主动添加一个空实现 640 | //iOS8以上 641 | - (void)collectionView:(UICollectionView *)collectionView willDisplaySupplementaryView:(UICollectionReusableView *)view forElementKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath 642 | { 643 | 644 | } 645 | //iOS8以下 646 | - (void)collectionView:(UICollectionView *)collectionView didEndDisplayingSupplementaryView:(UICollectionReusableView *)view forElementOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath 647 | { 648 | } 649 | 650 | - (void)CHD_collectionView:(UICollectionView *)collectionView willDisplaySupplementaryView:(UICollectionReusableView *)view forElementKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath 651 | { 652 | [self CHD_collectionView:collectionView willDisplaySupplementaryView:view forElementKind:elementKind atIndexPath:indexPath]; 653 | UIColor *sectionViewColor = chd_collection_header_color; 654 | NSString *Kind = @"Header"; 655 | if ([elementKind isEqualToString:UICollectionElementKindSectionFooter]) { 656 | sectionViewColor = chd_collection_footer_color; 657 | Kind = @"Footer"; 658 | }else if ([elementKind isEqualToString:@"HDDecorationViewKind"]){ 659 | Kind = @"Decoration(装饰)"; 660 | sectionViewColor = chd_collection_decoration_color; 661 | } 662 | UILabel *hover = [view hoverView:sectionViewColor]; 663 | 664 | hover.attributedText = [CHD_MustrHelper getMustr:[NSString stringWithFormat:@"%@++%@++%@",Kind,NSStringFromClass([view class]),@(indexPath.section)] textColor:chd_collection_text_color backGroundColor:[sectionViewColor colorWithAlphaComponent:chd_collection_bg_alpha]]; 665 | hover.hidden = ![CHD_HookHelper shareInstance].is_open_chdCollection; 666 | } 667 | 668 | - (void)CHD_collectionView:(UICollectionView *)collectionView didEndDisplayingSupplementaryView:(UICollectionReusableView *)view forElementOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath 669 | { 670 | [self CHD_collectionView:collectionView didEndDisplayingSupplementaryView:view forElementOfKind:elementKind atIndexPath:indexPath]; 671 | UIColor *sectionViewColor = chd_collection_header_color; 672 | NSString *Kind = @"Header"; 673 | if ([elementKind isEqualToString:UICollectionElementKindSectionFooter]) { 674 | sectionViewColor = chd_collection_footer_color; 675 | Kind = @"Footer"; 676 | }else if ([elementKind isEqualToString:@"HDDecorationViewKind"]){ 677 | Kind = @"Decoration(装饰)"; 678 | sectionViewColor = chd_collection_decoration_color; 679 | } 680 | UILabel *hover = [view hoverView:sectionViewColor]; 681 | 682 | hover.attributedText = [CHD_MustrHelper getMustr:[NSString stringWithFormat:@"%@++%@++%@",Kind,NSStringFromClass([view class]),@(indexPath.section)] textColor:chd_collection_text_color backGroundColor:[sectionViewColor colorWithAlphaComponent:chd_collection_bg_alpha]]; 683 | hover.hidden = ![CHD_HookHelper shareInstance].is_open_chdCollection; 684 | } 685 | 686 | 687 | 688 | @end 689 | 690 | 691 | 692 | 693 | 694 | 695 | 696 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 donggelaile 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. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # CHD_ListView_Structure 3 | ## 推荐一下[HDCollectionView](https://github.com/donggelaile/HDCollectionView) 近期写的数据驱动的滑动列表组件。基于flexBox,支持各种对齐方式、瀑布流、指定悬浮、分段布局、横向滑动等等 4 | ## 前言 5 | 大多数的iOS工程中,50%以上的页面是由**UITableView**或**UICollectionView**搭建的,这里统称为**ListView**。当我们接手已有项目时,我们如何快速的理清每个**ListView**的结构。或者,当我们自己写的某个页面过去很长时间时,产品过来告诉我们某个页面的某个位置需要调整,可能自己也是依稀记得哪部分是段头,哪部分是断尾,总要花一些时间来对应相应的区块。**CHD_ListView_Structure**正是为了让你快速的区分每个**ListView**的页面结构而生的。 6 | ## 使用 7 | #### 方式一 8 | 直接下载源代码,在**Appdelegate**导入h文件,然后调用 9 | ``` 10 | [CHD_ListView_Structure openStructureShow_TableV:YES collectionV:YES]; 11 | ``` 12 | 即可。 13 | #### 方式二 14 | ``` 15 | pod 'CHD_ListView_Structure' 16 | ``` 17 | 然后导入头文件并调用上面的方法。 18 | ## 特征 19 | * 支持**UITableView**和**UICollectionView**结构查看 20 | * 支持只查看二者中一个,关闭另一个 21 | * 无侵入,无需继承,一句话开启关闭 22 | * 无论开启或关闭,只在**DEBUG**模式下生效 23 | * **Header**、**Cell**、**Footer**使用不同颜色线框包围,并在其上展示类名及其**Index** 24 | * 提供一个简单的可拖动的**Toggle**按钮,实时隐藏或显示**ListView**的结构 25 | * 简单的内存泄漏判断依据(点击**Toggle**按钮时会打印当前存活的**ListView**总个数,当你返回到上一页面再次点击**Toggle**按钮时,如果个数未减少,那么可能存在内存泄漏) 26 | ## 效果 27 | #### 1、百思不得姐(高仿) 28 | 对开源项目[百思不得姐(高仿)](https://github.com/targetcloud/baisibudejie)做了结构分析,其中部分效果图如下: 29 | ![](https://github.com/donggelaile/CHD_ListView_Structure/blob/master/ScreenShots/BSBDJ/IMG_1663.PNG?raw=true) 30 | ![](https://github.com/donggelaile/CHD_ListView_Structure/blob/master/ScreenShots/BSBDJ/IMG_1664.PNG?raw=true) 31 | ![](https://github.com/donggelaile/CHD_ListView_Structure/blob/master/ScreenShots/BSBDJ/IMG_1665.PNG?raw=true) 32 | ![](https://github.com/donggelaile/CHD_ListView_Structure/blob/master/ScreenShots/BSBDJ/IMG_1666.PNG?raw=true) 33 | 34 | #### 2、网易云阅读 35 | 借助神奇的工具[IPAPatch](https://github.com/Naituw/IPAPatch)来看下大厂是如何使用tableView的,部分页面如下: 36 | ![](https://github.com/donggelaile/CHD_ListView_Structure/blob/master/ScreenShots/WYYYD/IMG_1667.PNG?raw=true) 37 | ![](https://github.com/donggelaile/CHD_ListView_Structure/blob/master/ScreenShots/WYYYD/IMG_1668.PNG?raw=true) 38 | ![](https://github.com/donggelaile/CHD_ListView_Structure/blob/master/ScreenShots/WYYYD/IMG_1669.PNG?raw=true) 39 | ![](https://github.com/donggelaile/CHD_ListView_Structure/blob/master/ScreenShots/WYYYD/IMG_1670.PNG?raw=true) 40 | 41 | 42 | ## 其他 43 | 如有问题,还请指正,共同进步。如果对您有所帮助,希望给颗✨✨(即使现在不用,收藏起来也是极好的) 44 | ## LICENSE 45 | MIT 46 | -------------------------------------------------------------------------------- /ScreenShots/BSBDJ/IMG_1663.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/donggelaile/CHD_ListView_Structure/928d5adc746b318e6ab0f9e988cd1cad352467fe/ScreenShots/BSBDJ/IMG_1663.PNG -------------------------------------------------------------------------------- /ScreenShots/BSBDJ/IMG_1664.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/donggelaile/CHD_ListView_Structure/928d5adc746b318e6ab0f9e988cd1cad352467fe/ScreenShots/BSBDJ/IMG_1664.PNG -------------------------------------------------------------------------------- /ScreenShots/BSBDJ/IMG_1665.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/donggelaile/CHD_ListView_Structure/928d5adc746b318e6ab0f9e988cd1cad352467fe/ScreenShots/BSBDJ/IMG_1665.PNG -------------------------------------------------------------------------------- /ScreenShots/BSBDJ/IMG_1666.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/donggelaile/CHD_ListView_Structure/928d5adc746b318e6ab0f9e988cd1cad352467fe/ScreenShots/BSBDJ/IMG_1666.PNG -------------------------------------------------------------------------------- /ScreenShots/WYYYD/IMG_1667.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/donggelaile/CHD_ListView_Structure/928d5adc746b318e6ab0f9e988cd1cad352467fe/ScreenShots/WYYYD/IMG_1667.PNG -------------------------------------------------------------------------------- /ScreenShots/WYYYD/IMG_1668.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/donggelaile/CHD_ListView_Structure/928d5adc746b318e6ab0f9e988cd1cad352467fe/ScreenShots/WYYYD/IMG_1668.PNG -------------------------------------------------------------------------------- /ScreenShots/WYYYD/IMG_1669.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/donggelaile/CHD_ListView_Structure/928d5adc746b318e6ab0f9e988cd1cad352467fe/ScreenShots/WYYYD/IMG_1669.PNG -------------------------------------------------------------------------------- /ScreenShots/WYYYD/IMG_1670.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/donggelaile/CHD_ListView_Structure/928d5adc746b318e6ab0f9e988cd1cad352467fe/ScreenShots/WYYYD/IMG_1670.PNG --------------------------------------------------------------------------------