├── README.md └── UITableView + NoData ├── UITableView + NoData.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcuserdata │ │ └── clarence.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ └── clarence.xcuserdatad │ ├── xcdebugger │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ ├── UITableView + NoData.xcscheme │ └── xcschememanagement.plist └── UITableView + NoData ├── AppDelegate.h ├── AppDelegate.m ├── Assets.xcassets └── AppIcon.appiconset │ └── Contents.json ├── Base.lproj ├── LaunchScreen.storyboard └── Main.storyboard ├── Images ├── 1.jpg ├── 2.gif └── test.gif ├── Info.plist ├── UITableView+fl_category.h ├── UITableView+fl_category.m ├── ViewController.h ├── ViewController.m ├── fluidicon.jpeg └── main.m /README.md: -------------------------------------------------------------------------------- 1 | ##一、前言 2 | **1、之前写了一篇 [[UIView 的分类,一句代码显示无数据界面](http://www.jianshu.com/p/7e5c6caf28d4)](http://www.jianshu.com/p/7e5c6caf28d4) ,如果针对 tableView 或者 collectionView 使用起来还是挺麻烦的,简单分析一下吧** 3 | - 优点: 适用范围比较广泛,只要界面是 `UIView 或 其子类` ,都适用 4 | 5 | - 缺点: 6 | - 需要调用者手动管理(创建显示和隐藏),使用起来不方便。 7 | - 没有针对无网络进行封装,需要调用者在外界自己判断 8 | 9 | **2、先来看看本框架达到的效果吧,授人以鱼不如授人以渔!简单功能,本文做了详细分析,开源的更多是封装思想,所以文字比较多,请做好心理准备,但绝对有所收获的** 10 | 11 | 12 | ![无网络显示界面,动态适配](http://upload-images.jianshu.io/upload_images/1085031-20e46e1dca6699ec.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/500) 13 | 14 | 15 | ![无数据显示界面,动态适配](http://upload-images.jianshu.io/upload_images/1085031-020d4e87cc6918ac.gif?imageMogr2/auto-orient/strip) 16 | 17 | 18 | 19 | ##二、分析思考 20 | - 1、实际项目中使用显示无数据或者无网络界面 ,一般都是 `UITableView` 或者 `UICollectionView` 此时如果使用 [[UIView 的分类](http://www.jianshu.com/p/7e5c6caf28d4)](http://www.jianshu.com/p/7e5c6caf28d4) 相对麻烦很多,如果是给旧项目添加这个功能,修改量就很大了,因为需要手动管理显示和隐藏 21 | 22 | - 2、那么如何避免手动管理呢?考虑到 `UITableView` 和 `UICollectionView` 两个都有 `reloadData` 方法,调用一次就会重新重新执行 `dataSource` 数据源方法,实际项目中,我们请求网络拿到列表数据后,都需要调用 `reloadData` ,而恰恰这个时候,为了更好的用户体验,我们也需要处理是否无数据或者无网络,如果没网络,需要显示无网络界面;而无数据就要显示无数据界面,那能不能在 `reloadData` 方法里面就处理了,或许你已经想到了 23 | 24 | - 3、对的,用runtime 替换掉tableView 或者 collectionView 的 `reloadData` 方法,然后在替换的方法里面处理好显示界面的逻辑,此时每当执行 `reloadData` 的时候,就自动判断需要显示什么界面,调用者不需要手动管理 25 | 26 | - 4、要替换系统的 `reloadData` 方法,有两种方式,分类和继承,原理都一样,本文就使用分类对UITableView 进行分析,当然UICollectionView 也是一样的,思路一样,如果需要,大家可自行实现 27 | 28 | - ###5、需要什么样的功能 29 | - **(1)参考不同的app,有些 app 显示无数据界面是一张gif 图,当然主流的都是 静态图 ,因此必须支持静图和动图的显示** 30 | 31 | - **(2)图片数据一般来自本地,但有可能来自网络(后台可以随时更换显示的无数据图,更新维护相对方便),因此必须要支持网络url下载,当然,为了更好的用户体验,网络图片下载后都需要缓存起来,下次就不需要再请求网络,而且,本地的gif也需要缓存到内存中,为了加快读取速度,可以参考SDWebImage,内存和沙盒都缓存起来,先从内存中获取,没有再从沙盒中获取,再没有才请求网络;既然有缓存,肯定也需要清空缓存** 32 | 33 | - ** (3)考虑到此时可能会显示或者隐藏 `UINavigationBar` 或 `UITabBar` ,那么这个无数据或无网络界面也需要动态更新布局,填充界面,不能留空白** 34 | 35 | - **(4)当然还需要处理点击事件,考虑到分类拓展性不强,因此默认是整个界面点击,如果你是用继承实现,这就好办,还可以提供自定义界面(custom view)等等,本文就不作分析了** 36 | 37 | ##三、API 设计 38 | >1、是否开启缓存,默认开启,开启后,会缓存到沙盒 以及 内存,如果是本地gif图片,也会缓存到内存 39 | 40 | ``` 41 | /** 42 |  *  @author gitKong 43 |  * 44 |  *  是否开启自动缓存,此时会缓存到沙盒 和 内存中,默认开启 45 |  */ 46 | @property (nonatomic,assign)BOOL fl_autoCache; 47 | ``` 48 | 49 | >2、没有数据显示的图片,不能为nil(内部有断言),可以传入本地图片名 或者 网络URL (包括gif,如果本地gif 图,需要加上后缀) 50 | 51 | ``` 52 | /** 53 |  *  @author gitKong 54 |  * 55 |  *  没有数据显示的图片,不能为nil 56 |  * 57 |  *  可传入 本地图片名 或者 网络URL (包括gif) 58 |  */ 59 | @property (nonatomic,copy)NSString *fl_noData_image; 60 | ``` 61 | 62 | >3、没有网络显示的图片,不能为nil(内部有断言),可以传入本地图片名 或者 网络URL (包括gif,如果本地gif 图,需要加上后缀) 63 | 64 | ``` 65 | /** 66 |  *  @author gitKong 67 |  * 68 |  *  没有网络显示的图片,不能为nil 69 |  * 70 |  *  可传入 本地图片名 或者 网络URL (包括gif) 71 |  */ 72 | @property (nonatomic,copy)NSString *fl_noNetwork_image; 73 | ``` 74 | 75 | >4、没有网络或者没有数据显示界面的点击事件,默认是整个界面的点击响应。如果自定义需求比较大,建议使用继承实现。 76 | 77 | ``` 78 | /** 79 |  *  @author gitKong 80 |  * 81 |  *  没有网络或者没有数据显示界面的点击事件 82 |  */ 83 | - (void)fl_imageViewClickOperation:(void(^)())clickOperation; 84 | ``` 85 | 86 | >5、清空缓存,包括沙盒 和 内存中的都会清空,如果需要单独清空,可以从 实现文件 中开放出来 87 | 88 | ``` 89 | /** 90 |  *  @author gitKong 91 |  * 92 |  *  清空缓存(包括沙盒和内存) 93 |  */ 94 | - (void)fl_clearCache; 95 | ``` 96 | 97 | ##四、关键代码分析 98 | 99 | > 1、**Swizzling方法替换**,在load 方法(load是只要类所在文件被引用就会被调用)中实现,如果方法存在那么直接替换方法,如果不存在则交换方法实现,替换tableView的 `reloadData` 方法,内部处理是否有网络或者有数据显示的界面 100 | 101 | ``` 102 | + (void)fl_methodSwizzlingWithOriginalSelector:(SEL)originalSelector bySwizzledSelector:(SEL)swizzledSelector{ 103 |     Class class = [self class]; 104 |     Method originalMethod = class_getInstanceMethod(class, originalSelector); 105 |     Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector); 106 |     BOOL didAddMethod = class_addMethod(class,originalSelector, 107 |                                         method_getImplementation(swizzledMethod), 108 |                                         method_getTypeEncoding(swizzledMethod)); 109 |     if (didAddMethod) { 110 |         class_replaceMethod(class,swizzledSelector, 111 |                             method_getImplementation(originalMethod), 112 |                             method_getTypeEncoding(originalMethod)); 113 |     } 114 |     else { 115 |         method_exchangeImplementations(originalMethod, swizzledMethod); 116 |     } 117 | } 118 | ``` 119 | 120 | > 2、**判断网络状态**,考虑到如果使用 `Reachability` 需要导入文件,有一定的耦合性,不方便移植,因此本框架是通过获取状态栏的信息来判断,通过 runtime && KVC 就很容易获取状态栏的信息(runtime 可以知道 `UIStatusBar` 的所有属性信息,KVC 进行属性操作),经测试发现,飞行模式和关闭移动网络都拿不到 `dataNetworkType` 属性信息,1 - 2G; 2 - 3G; 3 - 4G; 5 - WIFI 121 | 122 | ``` 123 | - (BOOL)checkNoNetwork{ 124 |     BOOL flag = NO; 125 |     UIApplication *app = [UIApplication sharedApplication]; 126 |     NSArray *children = [[[app valueForKeyPath:@"statusBar"] valueForKeyPath:@"foregroundView"] subviews]; 127 |     int netType = 0; 128 |     //获取到网络返回码 129 |     for (id child in children) { 130 |         if ([child isKindOfClass:NSClassFromString(@"UIStatusBarDataNetworkItemView")]) { 131 |             //获取到状态栏,飞行模式和关闭移动网络都拿不到dataNetworkType;1 - 2G; 2 - 3G; 3 - 4G; 5 - WIFI 132 |             netType = [[child valueForKeyPath:@"dataNetworkType"] intValue]; 133 |              134 |             switch (netType) { 135 |                 case 0: 136 |                     flag = NO; 137 |                     //无网模式 138 |                     break; 139 |                      140 |                 default: 141 |                     flag = YES; 142 |                     break; 143 |             } 144 |         } 145 |     } 146 |     return flag; 147 | } 148 | ``` 149 | 150 | > 3、**判断是否有数据** 直接通过 `dataSource` 获取对应的 `section` 和 `row` 进行判断,只要 `row` 不为空,那么就证明有数据 151 | 152 | ``` 153 | - (BOOL)checkNoData{ 154 |     NSInteger sections = 1; 155 |     NSInteger row = 0; 156 |     BOOL isEmpty = YES; 157 |     if ([self.dataSource respondsToSelector:@selector(numberOfSectionsInTableView:)]) { 158 |         sections = [self.dataSource numberOfSectionsInTableView:self]; 159 |     } 160 |     for (NSInteger section = 0; section < sections; section++) { 161 |         if ([self.dataSource respondsToSelector:@selector(tableView:numberOfRowsInSection:)]) { 162 |             row = [self.dataSource tableView:self numberOfRowsInSection:section]; 163 |             if (row) { 164 |                 // 只要有值都不是空 165 |                 isEmpty = NO; 166 |             } 167 |             else{ 168 |                 isEmpty = YES; 169 |             } 170 |         } 171 |     } 172 |     return isEmpty; 173 | } 174 | ``` 175 | 176 | > 4、**判断NavigationBar和TabBar 显示隐藏**,更新界面的布局,填充不留空白 177 | - 判断NavigationBar (提供三种方案) 178 | - 通过 `runtime` 发现 `UITableView` 有 一个隐藏属性 `visibleBounds` ,直译过来就是可视区域,通过实测,如果没有导航控制器,那么 `visibleBounds` 的`y = 0`,如果有导航控制器,而且`UINavigationBar` 是显示,那么`y = -64`,如果有导航控制器,但`UINavigationBar`隐藏,那么`y = -20`可以通过这个来判断导航栏是否隐藏; 179 | - 当然这个确实麻烦点,可以使用 我之前的文章 [任意NSObject及其子类中获取当前显示的控制器](http://www.jianshu.com/p/dcd26e1ab30f) 此时可以获取当前显示的控制器,然后判断NavigationBar 显示隐藏 180 | - 当然,还有一种办法,不需要去手动判断, `UITableView` 有 还有一个隐藏属性 `wrapperView` 这个 view 可以在 `debug view Hieratrchy` 里面看到层级结构,通过实测,这个会随着导航栏显示隐藏 来改变 y 的偏移,因此直接将无数据或者无网络页面添加到 `wrapperView` 上就可以了 181 | - 判断TabBar:本来打算通过 `[UITabBar appearance]` 来获取,发现虽然不会报错,但测试发现没任何效果,通过断点po提示 `<_UIAppearance:0x17025b000> with invocations (null)>` 是空的,不能获取到,当然 `[UINavigationBar appearance]` 也没效果,所以此时使用 [任意NSObject及其子类中获取当前显示的控制器](http://www.jianshu.com/p/dcd26e1ab30f) 来判断TabBar是否显示 182 | 183 | ``` 184 | - (void)updataImageViewFrame{ 185 |     // 如果没有导航控制器,那么rect的y值为0,如果有导航控制器,那么y为-64,如果导航控制器hidden那么也会跟着变,不需要额外修改 186 |     Class conecreteValue = NSClassFromString(@"NSConcreteValue"); 187 |     id concreteV = [[conecreteValue alloc] init]; 188 |     concreteV = [self valueForKey:@"visibleBounds"]; 189 |     CGRect rect ; 190 |     [concreteV getValue:&rect]; 191 |      192 |     // 判断是否有tabBar显示 193 |     // 注意:分类中使用[UITabBar appearance] 和 [UINavigationBar appearance] 都不能获取对象,断点po提示<_UIAppearance:0x17025b000> with invocations (null)> 194 |     UIViewController *currentVc = [self fl_viewController]; 195 |     UITabBarController *tabVc = (UITabBarController *)currentVc.tabBarController; 196 |     if (tabVc) { 197 |         self.imageView.frame = CGRectMake(rect.origin.x, 0, rect.size.width, rect.size.height + rect.origin.y - (tabVc.tabBar.hidden ? 0 : tabVc.tabBar.bounds.size.height)); 198 |     } 199 |     else{ 200 |         self.imageView.frame = CGRectMake(rect.origin.x, 0, rect.size.width, rect.size.height + rect.origin.y); 201 |     } 202 | } 203 | ``` 204 | 205 | >5、**获取GIF 图片 每一帧播放时长**,通过一个key `kCGImagePropertyGIFUnclampedDelayTime` 可以获取,然后拼接起来,播放GIF 图片 206 | 207 | ``` 208 | - (CGFloat)durationWithSource:(CGImageSourceRef)source atIndex:(NSUInteger)index { 209 |     float duration = 0.1f; 210 |     CFDictionaryRef propertiesRef = CGImageSourceCopyPropertiesAtIndex(source, index, nil); 211 |     NSDictionary *properties = (__bridge NSDictionary *)propertiesRef; 212 |     NSDictionary *gifProperties = properties[(NSString *)kCGImagePropertyGIFDictionary]; 213 |      214 |     NSNumber *delayTime = gifProperties[(NSString *)kCGImagePropertyGIFUnclampedDelayTime]; 215 |     if (delayTime) duration = delayTime.floatValue; 216 |     else { 217 |         delayTime = gifProperties[(NSString *)kCGImagePropertyGIFDelayTime]; 218 |         if (delayTime) duration = delayTime.floatValue; 219 |     } 220 |     CFRelease(propertiesRef); 221 |     return duration; 222 | } 223 | ``` 224 | 225 | ##五、总结 226 | 227 | - 1、加载GIF 图片内存占用挺大,特别是缓存到内存中,内存会飙升,注意使用,测试发现`SDWebImage` 也会出现内存飙升,`YYImageCache` 的话就优化很多,待优化 228 | 229 | - 2、分类中使用 `[UITabBar appearance]` 和 `[UINavigationBar appearance]` 都不能获取对象,断点po提示`<_UIAppearance:0x17025b000> with invocations (null)>` 230 | 231 | - 3、因为判断网络是通过获取状态栏信息来判断,如果 是 CMCC 连接的WI-FI,就不能正确判断网络是否已联网 232 | 233 | - 4、此框架零耦合,方便移植,使用方便,只需要设置 `fl_noData_image` 和 `fl_noNetwork_image` ,只要调用 `reloadData` 就会自动判断需要显示什么界面 234 | 235 | - 4、上文中提到的功能点都实现了,简单的功能,但做了详细的分析,从需求确定-功能分析-技术实现都做了详细的分析,封装的思想才是关键,开源不单单是代码,更多的是封装的思想 236 | 237 | - 5、具体实现代码比较多,本文就不一一详细讲解,Demo 中有 对应的注释,**欢迎大家去[我的简书](http://www.jianshu.com/users/fe5700cfb223/latest_articles)关注我,喜欢给个like 和 star**,会随时开源~ 238 | -------------------------------------------------------------------------------- /UITableView + NoData/UITableView + NoData.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 11526BDF1DF7D64500B30956 /* test.gif in Resources */ = {isa = PBXBuildFile; fileRef = 11526BDE1DF7D64500B30956 /* test.gif */; }; 11 | 117B02AC1DF50A7A00876FB4 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 117B02AB1DF50A7A00876FB4 /* main.m */; }; 12 | 117B02AF1DF50A7A00876FB4 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 117B02AE1DF50A7A00876FB4 /* AppDelegate.m */; }; 13 | 117B02B21DF50A7A00876FB4 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 117B02B11DF50A7A00876FB4 /* ViewController.m */; }; 14 | 117B02B51DF50A7A00876FB4 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 117B02B31DF50A7A00876FB4 /* Main.storyboard */; }; 15 | 117B02B71DF50A7A00876FB4 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 117B02B61DF50A7A00876FB4 /* Assets.xcassets */; }; 16 | 117B02BA1DF50A7A00876FB4 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 117B02B81DF50A7A00876FB4 /* LaunchScreen.storyboard */; }; 17 | 117B02C91DF5111100876FB4 /* fluidicon.jpeg in Resources */ = {isa = PBXBuildFile; fileRef = 117B02C81DF5111100876FB4 /* fluidicon.jpeg */; }; 18 | 117B02CC1DF5203800876FB4 /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 117B02CB1DF5203800876FB4 /* SystemConfiguration.framework */; }; 19 | 117B02F61DF5660400876FB4 /* 1.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 117B02F11DF5660400876FB4 /* 1.jpg */; }; 20 | 117B02F71DF5660400876FB4 /* 2.gif in Resources */ = {isa = PBXBuildFile; fileRef = 117B02F21DF5660400876FB4 /* 2.gif */; }; 21 | 117B02FE1DF5754100876FB4 /* UITableView+fl_category.m in Sources */ = {isa = PBXBuildFile; fileRef = 117B02FD1DF5754100876FB4 /* UITableView+fl_category.m */; }; 22 | 128D173D408973185E44A701 /* libPods-UITableView + NoData.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 8B33B6010F06E9381A1DD7B4 /* libPods-UITableView + NoData.a */; }; 23 | /* End PBXBuildFile section */ 24 | 25 | /* Begin PBXFileReference section */ 26 | 11526BDE1DF7D64500B30956 /* test.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; path = test.gif; sourceTree = ""; }; 27 | 117B02A71DF50A7900876FB4 /* UITableView + NoData.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "UITableView + NoData.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 28 | 117B02AB1DF50A7A00876FB4 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 29 | 117B02AD1DF50A7A00876FB4 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 30 | 117B02AE1DF50A7A00876FB4 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 31 | 117B02B01DF50A7A00876FB4 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 32 | 117B02B11DF50A7A00876FB4 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 33 | 117B02B41DF50A7A00876FB4 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 34 | 117B02B61DF50A7A00876FB4 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 35 | 117B02B91DF50A7A00876FB4 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 36 | 117B02BB1DF50A7A00876FB4 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 37 | 117B02C81DF5111100876FB4 /* fluidicon.jpeg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; name = fluidicon.jpeg; path = ../fluidicon.jpeg; sourceTree = ""; }; 38 | 117B02CB1DF5203800876FB4 /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.framework; sourceTree = SDKROOT; }; 39 | 117B02F11DF5660400876FB4 /* 1.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = 1.jpg; sourceTree = ""; }; 40 | 117B02F21DF5660400876FB4 /* 2.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; path = 2.gif; sourceTree = ""; }; 41 | 117B02FC1DF5754100876FB4 /* UITableView+fl_category.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UITableView+fl_category.h"; sourceTree = ""; }; 42 | 117B02FD1DF5754100876FB4 /* UITableView+fl_category.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UITableView+fl_category.m"; sourceTree = ""; }; 43 | 5F5C0C741082FC976F8BFBE3 /* Pods-UITableView + NoData.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-UITableView + NoData.debug.xcconfig"; path = "Pods/Target Support Files/Pods-UITableView + NoData/Pods-UITableView + NoData.debug.xcconfig"; sourceTree = ""; }; 44 | 7629C1BFAFDFCCB093897A26 /* Pods-UITableView + NoData.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-UITableView + NoData.release.xcconfig"; path = "Pods/Target Support Files/Pods-UITableView + NoData/Pods-UITableView + NoData.release.xcconfig"; sourceTree = ""; }; 45 | 8B33B6010F06E9381A1DD7B4 /* libPods-UITableView + NoData.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-UITableView + NoData.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 46 | /* End PBXFileReference section */ 47 | 48 | /* Begin PBXFrameworksBuildPhase section */ 49 | 117B02A41DF50A7900876FB4 /* Frameworks */ = { 50 | isa = PBXFrameworksBuildPhase; 51 | buildActionMask = 2147483647; 52 | files = ( 53 | 117B02CC1DF5203800876FB4 /* SystemConfiguration.framework in Frameworks */, 54 | 128D173D408973185E44A701 /* libPods-UITableView + NoData.a in Frameworks */, 55 | ); 56 | runOnlyForDeploymentPostprocessing = 0; 57 | }; 58 | /* End PBXFrameworksBuildPhase section */ 59 | 60 | /* Begin PBXGroup section */ 61 | 117B029E1DF50A7900876FB4 = { 62 | isa = PBXGroup; 63 | children = ( 64 | 117B02A91DF50A7900876FB4 /* UITableView + NoData */, 65 | 117B02A81DF50A7900876FB4 /* Products */, 66 | 117B02CA1DF5203700876FB4 /* Frameworks */, 67 | A799EE9EC5648237DA1823F4 /* Pods */, 68 | ); 69 | sourceTree = ""; 70 | }; 71 | 117B02A81DF50A7900876FB4 /* Products */ = { 72 | isa = PBXGroup; 73 | children = ( 74 | 117B02A71DF50A7900876FB4 /* UITableView + NoData.app */, 75 | ); 76 | name = Products; 77 | sourceTree = ""; 78 | }; 79 | 117B02A91DF50A7900876FB4 /* UITableView + NoData */ = { 80 | isa = PBXGroup; 81 | children = ( 82 | 117B02FB1DF574F000876FB4 /* UITableView + fl_category */, 83 | 117B02AD1DF50A7A00876FB4 /* AppDelegate.h */, 84 | 117B02AE1DF50A7A00876FB4 /* AppDelegate.m */, 85 | 117B02B01DF50A7A00876FB4 /* ViewController.h */, 86 | 117B02B11DF50A7A00876FB4 /* ViewController.m */, 87 | 117B02B31DF50A7A00876FB4 /* Main.storyboard */, 88 | 117B02B61DF50A7A00876FB4 /* Assets.xcassets */, 89 | 117B02F01DF5660400876FB4 /* Images */, 90 | 117B02B81DF50A7A00876FB4 /* LaunchScreen.storyboard */, 91 | 117B02BB1DF50A7A00876FB4 /* Info.plist */, 92 | 117B02AA1DF50A7900876FB4 /* Supporting Files */, 93 | ); 94 | path = "UITableView + NoData"; 95 | sourceTree = ""; 96 | }; 97 | 117B02AA1DF50A7900876FB4 /* Supporting Files */ = { 98 | isa = PBXGroup; 99 | children = ( 100 | 117B02AB1DF50A7A00876FB4 /* main.m */, 101 | ); 102 | name = "Supporting Files"; 103 | sourceTree = ""; 104 | }; 105 | 117B02CA1DF5203700876FB4 /* Frameworks */ = { 106 | isa = PBXGroup; 107 | children = ( 108 | 117B02CB1DF5203800876FB4 /* SystemConfiguration.framework */, 109 | 8B33B6010F06E9381A1DD7B4 /* libPods-UITableView + NoData.a */, 110 | ); 111 | name = Frameworks; 112 | sourceTree = ""; 113 | }; 114 | 117B02F01DF5660400876FB4 /* Images */ = { 115 | isa = PBXGroup; 116 | children = ( 117 | 11526BDE1DF7D64500B30956 /* test.gif */, 118 | 117B02C81DF5111100876FB4 /* fluidicon.jpeg */, 119 | 117B02F11DF5660400876FB4 /* 1.jpg */, 120 | 117B02F21DF5660400876FB4 /* 2.gif */, 121 | ); 122 | path = Images; 123 | sourceTree = ""; 124 | }; 125 | 117B02FB1DF574F000876FB4 /* UITableView + fl_category */ = { 126 | isa = PBXGroup; 127 | children = ( 128 | 117B02FC1DF5754100876FB4 /* UITableView+fl_category.h */, 129 | 117B02FD1DF5754100876FB4 /* UITableView+fl_category.m */, 130 | ); 131 | name = "UITableView + fl_category"; 132 | sourceTree = ""; 133 | }; 134 | A799EE9EC5648237DA1823F4 /* Pods */ = { 135 | isa = PBXGroup; 136 | children = ( 137 | 5F5C0C741082FC976F8BFBE3 /* Pods-UITableView + NoData.debug.xcconfig */, 138 | 7629C1BFAFDFCCB093897A26 /* Pods-UITableView + NoData.release.xcconfig */, 139 | ); 140 | name = Pods; 141 | sourceTree = ""; 142 | }; 143 | /* End PBXGroup section */ 144 | 145 | /* Begin PBXNativeTarget section */ 146 | 117B02A61DF50A7900876FB4 /* UITableView + NoData */ = { 147 | isa = PBXNativeTarget; 148 | buildConfigurationList = 117B02BE1DF50A7A00876FB4 /* Build configuration list for PBXNativeTarget "UITableView + NoData" */; 149 | buildPhases = ( 150 | E8982F8B0A5D49640C0B022D /* [CP] Check Pods Manifest.lock */, 151 | 117B02A31DF50A7900876FB4 /* Sources */, 152 | 117B02A41DF50A7900876FB4 /* Frameworks */, 153 | 117B02A51DF50A7900876FB4 /* Resources */, 154 | D4055C6BA73F1B09206CE375 /* [CP] Embed Pods Frameworks */, 155 | FD44187DED77CD89128FFDED /* [CP] Copy Pods Resources */, 156 | ); 157 | buildRules = ( 158 | ); 159 | dependencies = ( 160 | ); 161 | name = "UITableView + NoData"; 162 | productName = "UITableView + NoData"; 163 | productReference = 117B02A71DF50A7900876FB4 /* UITableView + NoData.app */; 164 | productType = "com.apple.product-type.application"; 165 | }; 166 | /* End PBXNativeTarget section */ 167 | 168 | /* Begin PBXProject section */ 169 | 117B029F1DF50A7900876FB4 /* Project object */ = { 170 | isa = PBXProject; 171 | attributes = { 172 | LastUpgradeCheck = 0810; 173 | ORGANIZATIONNAME = gitKong; 174 | TargetAttributes = { 175 | 117B02A61DF50A7900876FB4 = { 176 | CreatedOnToolsVersion = 8.1; 177 | DevelopmentTeam = GB8FE8WWF8; 178 | ProvisioningStyle = Automatic; 179 | }; 180 | }; 181 | }; 182 | buildConfigurationList = 117B02A21DF50A7900876FB4 /* Build configuration list for PBXProject "UITableView + NoData" */; 183 | compatibilityVersion = "Xcode 3.2"; 184 | developmentRegion = English; 185 | hasScannedForEncodings = 0; 186 | knownRegions = ( 187 | en, 188 | Base, 189 | ); 190 | mainGroup = 117B029E1DF50A7900876FB4; 191 | productRefGroup = 117B02A81DF50A7900876FB4 /* Products */; 192 | projectDirPath = ""; 193 | projectRoot = ""; 194 | targets = ( 195 | 117B02A61DF50A7900876FB4 /* UITableView + NoData */, 196 | ); 197 | }; 198 | /* End PBXProject section */ 199 | 200 | /* Begin PBXResourcesBuildPhase section */ 201 | 117B02A51DF50A7900876FB4 /* Resources */ = { 202 | isa = PBXResourcesBuildPhase; 203 | buildActionMask = 2147483647; 204 | files = ( 205 | 117B02BA1DF50A7A00876FB4 /* LaunchScreen.storyboard in Resources */, 206 | 117B02B71DF50A7A00876FB4 /* Assets.xcassets in Resources */, 207 | 117B02F71DF5660400876FB4 /* 2.gif in Resources */, 208 | 117B02B51DF50A7A00876FB4 /* Main.storyboard in Resources */, 209 | 11526BDF1DF7D64500B30956 /* test.gif in Resources */, 210 | 117B02C91DF5111100876FB4 /* fluidicon.jpeg in Resources */, 211 | 117B02F61DF5660400876FB4 /* 1.jpg in Resources */, 212 | ); 213 | runOnlyForDeploymentPostprocessing = 0; 214 | }; 215 | /* End PBXResourcesBuildPhase section */ 216 | 217 | /* Begin PBXShellScriptBuildPhase section */ 218 | D4055C6BA73F1B09206CE375 /* [CP] Embed Pods Frameworks */ = { 219 | isa = PBXShellScriptBuildPhase; 220 | buildActionMask = 2147483647; 221 | files = ( 222 | ); 223 | inputPaths = ( 224 | ); 225 | name = "[CP] Embed Pods Frameworks"; 226 | outputPaths = ( 227 | ); 228 | runOnlyForDeploymentPostprocessing = 0; 229 | shellPath = /bin/sh; 230 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-UITableView + NoData/Pods-UITableView + NoData-frameworks.sh\"\n"; 231 | showEnvVarsInLog = 0; 232 | }; 233 | E8982F8B0A5D49640C0B022D /* [CP] Check Pods Manifest.lock */ = { 234 | isa = PBXShellScriptBuildPhase; 235 | buildActionMask = 2147483647; 236 | files = ( 237 | ); 238 | inputPaths = ( 239 | ); 240 | name = "[CP] Check Pods Manifest.lock"; 241 | outputPaths = ( 242 | ); 243 | runOnlyForDeploymentPostprocessing = 0; 244 | shellPath = /bin/sh; 245 | shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; 246 | showEnvVarsInLog = 0; 247 | }; 248 | FD44187DED77CD89128FFDED /* [CP] Copy Pods Resources */ = { 249 | isa = PBXShellScriptBuildPhase; 250 | buildActionMask = 2147483647; 251 | files = ( 252 | ); 253 | inputPaths = ( 254 | ); 255 | name = "[CP] Copy Pods Resources"; 256 | outputPaths = ( 257 | ); 258 | runOnlyForDeploymentPostprocessing = 0; 259 | shellPath = /bin/sh; 260 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-UITableView + NoData/Pods-UITableView + NoData-resources.sh\"\n"; 261 | showEnvVarsInLog = 0; 262 | }; 263 | /* End PBXShellScriptBuildPhase section */ 264 | 265 | /* Begin PBXSourcesBuildPhase section */ 266 | 117B02A31DF50A7900876FB4 /* Sources */ = { 267 | isa = PBXSourcesBuildPhase; 268 | buildActionMask = 2147483647; 269 | files = ( 270 | 117B02B21DF50A7A00876FB4 /* ViewController.m in Sources */, 271 | 117B02AF1DF50A7A00876FB4 /* AppDelegate.m in Sources */, 272 | 117B02AC1DF50A7A00876FB4 /* main.m in Sources */, 273 | 117B02FE1DF5754100876FB4 /* UITableView+fl_category.m in Sources */, 274 | ); 275 | runOnlyForDeploymentPostprocessing = 0; 276 | }; 277 | /* End PBXSourcesBuildPhase section */ 278 | 279 | /* Begin PBXVariantGroup section */ 280 | 117B02B31DF50A7A00876FB4 /* Main.storyboard */ = { 281 | isa = PBXVariantGroup; 282 | children = ( 283 | 117B02B41DF50A7A00876FB4 /* Base */, 284 | ); 285 | name = Main.storyboard; 286 | sourceTree = ""; 287 | }; 288 | 117B02B81DF50A7A00876FB4 /* LaunchScreen.storyboard */ = { 289 | isa = PBXVariantGroup; 290 | children = ( 291 | 117B02B91DF50A7A00876FB4 /* Base */, 292 | ); 293 | name = LaunchScreen.storyboard; 294 | sourceTree = ""; 295 | }; 296 | /* End PBXVariantGroup section */ 297 | 298 | /* Begin XCBuildConfiguration section */ 299 | 117B02BC1DF50A7A00876FB4 /* Debug */ = { 300 | isa = XCBuildConfiguration; 301 | buildSettings = { 302 | ALWAYS_SEARCH_USER_PATHS = NO; 303 | CLANG_ANALYZER_NONNULL = YES; 304 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 305 | CLANG_CXX_LIBRARY = "libc++"; 306 | CLANG_ENABLE_MODULES = YES; 307 | CLANG_ENABLE_OBJC_ARC = YES; 308 | CLANG_WARN_BOOL_CONVERSION = YES; 309 | CLANG_WARN_CONSTANT_CONVERSION = YES; 310 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 311 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 312 | CLANG_WARN_EMPTY_BODY = YES; 313 | CLANG_WARN_ENUM_CONVERSION = YES; 314 | CLANG_WARN_INFINITE_RECURSION = YES; 315 | CLANG_WARN_INT_CONVERSION = YES; 316 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 317 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 318 | CLANG_WARN_UNREACHABLE_CODE = YES; 319 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 320 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 321 | COPY_PHASE_STRIP = NO; 322 | DEBUG_INFORMATION_FORMAT = dwarf; 323 | ENABLE_STRICT_OBJC_MSGSEND = YES; 324 | ENABLE_TESTABILITY = YES; 325 | GCC_C_LANGUAGE_STANDARD = gnu99; 326 | GCC_DYNAMIC_NO_PIC = NO; 327 | GCC_NO_COMMON_BLOCKS = YES; 328 | GCC_OPTIMIZATION_LEVEL = 0; 329 | GCC_PREPROCESSOR_DEFINITIONS = ( 330 | "DEBUG=1", 331 | "$(inherited)", 332 | ); 333 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 334 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 335 | GCC_WARN_UNDECLARED_SELECTOR = YES; 336 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 337 | GCC_WARN_UNUSED_FUNCTION = YES; 338 | GCC_WARN_UNUSED_VARIABLE = YES; 339 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 340 | MTL_ENABLE_DEBUG_INFO = YES; 341 | ONLY_ACTIVE_ARCH = YES; 342 | SDKROOT = iphoneos; 343 | }; 344 | name = Debug; 345 | }; 346 | 117B02BD1DF50A7A00876FB4 /* Release */ = { 347 | isa = XCBuildConfiguration; 348 | buildSettings = { 349 | ALWAYS_SEARCH_USER_PATHS = NO; 350 | CLANG_ANALYZER_NONNULL = YES; 351 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 352 | CLANG_CXX_LIBRARY = "libc++"; 353 | CLANG_ENABLE_MODULES = YES; 354 | CLANG_ENABLE_OBJC_ARC = YES; 355 | CLANG_WARN_BOOL_CONVERSION = YES; 356 | CLANG_WARN_CONSTANT_CONVERSION = YES; 357 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 358 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 359 | CLANG_WARN_EMPTY_BODY = YES; 360 | CLANG_WARN_ENUM_CONVERSION = YES; 361 | CLANG_WARN_INFINITE_RECURSION = YES; 362 | CLANG_WARN_INT_CONVERSION = YES; 363 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 364 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 365 | CLANG_WARN_UNREACHABLE_CODE = YES; 366 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 367 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 368 | COPY_PHASE_STRIP = NO; 369 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 370 | ENABLE_NS_ASSERTIONS = NO; 371 | ENABLE_STRICT_OBJC_MSGSEND = YES; 372 | GCC_C_LANGUAGE_STANDARD = gnu99; 373 | GCC_NO_COMMON_BLOCKS = YES; 374 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 375 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 376 | GCC_WARN_UNDECLARED_SELECTOR = YES; 377 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 378 | GCC_WARN_UNUSED_FUNCTION = YES; 379 | GCC_WARN_UNUSED_VARIABLE = YES; 380 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 381 | MTL_ENABLE_DEBUG_INFO = NO; 382 | SDKROOT = iphoneos; 383 | VALIDATE_PRODUCT = YES; 384 | }; 385 | name = Release; 386 | }; 387 | 117B02BF1DF50A7A00876FB4 /* Debug */ = { 388 | isa = XCBuildConfiguration; 389 | baseConfigurationReference = 5F5C0C741082FC976F8BFBE3 /* Pods-UITableView + NoData.debug.xcconfig */; 390 | buildSettings = { 391 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 392 | DEVELOPMENT_TEAM = GB8FE8WWF8; 393 | INFOPLIST_FILE = "UITableView + NoData/Info.plist"; 394 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 395 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 396 | PRODUCT_BUNDLE_IDENTIFIER = "gitKong.UITableView---NoData"; 397 | PRODUCT_NAME = "$(TARGET_NAME)"; 398 | }; 399 | name = Debug; 400 | }; 401 | 117B02C01DF50A7A00876FB4 /* Release */ = { 402 | isa = XCBuildConfiguration; 403 | baseConfigurationReference = 7629C1BFAFDFCCB093897A26 /* Pods-UITableView + NoData.release.xcconfig */; 404 | buildSettings = { 405 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 406 | DEVELOPMENT_TEAM = GB8FE8WWF8; 407 | INFOPLIST_FILE = "UITableView + NoData/Info.plist"; 408 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 409 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 410 | PRODUCT_BUNDLE_IDENTIFIER = "gitKong.UITableView---NoData"; 411 | PRODUCT_NAME = "$(TARGET_NAME)"; 412 | }; 413 | name = Release; 414 | }; 415 | /* End XCBuildConfiguration section */ 416 | 417 | /* Begin XCConfigurationList section */ 418 | 117B02A21DF50A7900876FB4 /* Build configuration list for PBXProject "UITableView + NoData" */ = { 419 | isa = XCConfigurationList; 420 | buildConfigurations = ( 421 | 117B02BC1DF50A7A00876FB4 /* Debug */, 422 | 117B02BD1DF50A7A00876FB4 /* Release */, 423 | ); 424 | defaultConfigurationIsVisible = 0; 425 | defaultConfigurationName = Release; 426 | }; 427 | 117B02BE1DF50A7A00876FB4 /* Build configuration list for PBXNativeTarget "UITableView + NoData" */ = { 428 | isa = XCConfigurationList; 429 | buildConfigurations = ( 430 | 117B02BF1DF50A7A00876FB4 /* Debug */, 431 | 117B02C01DF50A7A00876FB4 /* Release */, 432 | ); 433 | defaultConfigurationIsVisible = 0; 434 | defaultConfigurationName = Release; 435 | }; 436 | /* End XCConfigurationList section */ 437 | }; 438 | rootObject = 117B029F1DF50A7900876FB4 /* Project object */; 439 | } 440 | -------------------------------------------------------------------------------- /UITableView + NoData/UITableView + NoData.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /UITableView + NoData/UITableView + NoData.xcodeproj/project.xcworkspace/xcuserdata/clarence.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gitkong/UITableView-category/7fa17a5614f7052c148b420da9ac4a668d862fd9/UITableView + NoData/UITableView + NoData.xcodeproj/project.xcworkspace/xcuserdata/clarence.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /UITableView + NoData/UITableView + NoData.xcodeproj/xcuserdata/clarence.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 8 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /UITableView + NoData/UITableView + NoData.xcodeproj/xcuserdata/clarence.xcuserdatad/xcschemes/UITableView + NoData.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /UITableView + NoData/UITableView + NoData.xcodeproj/xcuserdata/clarence.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | UITableView + NoData.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 117B02A61DF50A7900876FB4 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /UITableView + NoData/UITableView + NoData/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // UITableView + NoData 4 | // 5 | // Created by clarence on 16/12/5. 6 | // Copyright © 2016年 gitKong. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /UITableView + NoData/UITableView + NoData/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // UITableView + NoData 4 | // 5 | // Created by clarence on 16/12/5. 6 | // Copyright © 2016年 gitKong. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | -(void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification{ 18 | 19 | if (notification) { 20 | NSDictionary *userInfo = notification.userInfo; 21 | NSString *obj = [userInfo objectForKey:@"key"]; 22 | NSLog(@"%@",obj); 23 | } 24 | } 25 | 26 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 27 | // Override point for customization after application launch. 28 | return YES; 29 | } 30 | 31 | 32 | - (void)applicationWillResignActive:(UIApplication *)application { 33 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 34 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 35 | } 36 | 37 | 38 | - (void)applicationDidEnterBackground:(UIApplication *)application { 39 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 40 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 41 | } 42 | 43 | 44 | - (void)applicationWillEnterForeground:(UIApplication *)application { 45 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 46 | } 47 | 48 | 49 | - (void)applicationDidBecomeActive:(UIApplication *)application { 50 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 51 | } 52 | 53 | 54 | - (void)applicationWillTerminate:(UIApplication *)application { 55 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 56 | } 57 | 58 | 59 | @end 60 | -------------------------------------------------------------------------------- /UITableView + NoData/UITableView + NoData/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 | "info" : { 45 | "version" : 1, 46 | "author" : "xcode" 47 | } 48 | } -------------------------------------------------------------------------------- /UITableView + NoData/UITableView + NoData/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 | 27 | 28 | -------------------------------------------------------------------------------- /UITableView + NoData/UITableView + NoData/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 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /UITableView + NoData/UITableView + NoData/Images/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gitkong/UITableView-category/7fa17a5614f7052c148b420da9ac4a668d862fd9/UITableView + NoData/UITableView + NoData/Images/1.jpg -------------------------------------------------------------------------------- /UITableView + NoData/UITableView + NoData/Images/2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gitkong/UITableView-category/7fa17a5614f7052c148b420da9ac4a668d862fd9/UITableView + NoData/UITableView + NoData/Images/2.gif -------------------------------------------------------------------------------- /UITableView + NoData/UITableView + NoData/Images/test.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gitkong/UITableView-category/7fa17a5614f7052c148b420da9ac4a668d862fd9/UITableView + NoData/UITableView + NoData/Images/test.gif -------------------------------------------------------------------------------- /UITableView + NoData/UITableView + NoData/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | NSAppTransportSecurity 38 | 39 | 40 | 41 | NSAllowsArbitraryLoads 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /UITableView + NoData/UITableView + NoData/UITableView+fl_category.h: -------------------------------------------------------------------------------- 1 | /* 2 | * author 孔凡列 3 | * 4 | * gitHub https://github.com/gitkong 5 | * cocoaChina http://code.cocoachina.com/user/ 6 | * 简书 http://www.jianshu.com/users/fe5700cfb223/latest_articles 7 | * QQ 279761135 8 | * 微信公众号 原创技术分享 9 | * 喜欢就给个like 和 star 喔~ 10 | */ 11 | 12 | #import 13 | 14 | @interface UITableView (fl_category) 15 | /** 16 | * @author gitKong 17 | * 18 | * 是否开启自动缓存,此时会缓存到沙盒 和 内存中,默认开启 19 | */ 20 | @property (nonatomic,assign)BOOL fl_autoCache; 21 | /** 22 | * @author gitKong 23 | * 24 | * 没有数据显示的图片,不能为nil 25 | * 26 | * 可传入 本地图片名 或者 网络URL (包括gif)如果是网络URL,内部自动缓存 27 | */ 28 | @property (nonatomic,copy)NSString *fl_noData_image; 29 | /** 30 | * @author gitKong 31 | * 32 | * 没有网络显示的图片,不能为nil 33 | * 34 | * 可传入 本地图片名 或者 网络URL (包括gif)如果是网络URL,内部自动缓存 35 | */ 36 | @property (nonatomic,copy)NSString *fl_noNetwork_image; 37 | /** 38 | * @author gitKong 39 | * 40 | * 没有网络或者没有数据显示界面的点击事件 41 | */ 42 | - (void)fl_imageViewClickOperation:(void(^)())clickOperation; 43 | /** 44 | * @author gitKong 45 | * 46 | * 清空缓存(包括沙盒和内存) 47 | */ 48 | - (void)fl_clearCache; 49 | 50 | @end 51 | -------------------------------------------------------------------------------- /UITableView + NoData/UITableView + NoData/UITableView+fl_category.m: -------------------------------------------------------------------------------- 1 | // 2 | // UITableView+fl_category.m 3 | // UITableView + NoData 4 | // 5 | // Created by clarence on 16/12/5. 6 | // Copyright © 2016年 gitKong. All rights reserved. 7 | // 8 | 9 | #import "UITableView+fl_category.h" 10 | #import 11 | #import 12 | //#import "Reachability.h" 13 | @interface UITableView () 14 | 15 | @property (nonatomic,strong)UIImageView *imageView; 16 | 17 | @property (nonatomic,copy)void(^clickOperation)(); 18 | 19 | @end 20 | 21 | @implementation UITableView (fl_category) 22 | /** 23 | * @author gitKong 24 | * 25 | * 沙盒路径 26 | */ 27 | static NSString *cache; 28 | /** 29 | * @author gitKong 30 | * 31 | * 内存缓存 32 | */ 33 | static NSCache *memory_cache; 34 | 35 | static BOOL autoCache; 36 | /** 37 | * @author gitKong 38 | * 39 | * load是只要类所在文件被引用就会被调用,而initialize是在类或者其子类的第一个方法被调用前调用。所以如果类没有被引用进项目,就不会有load调用;但即使类文件被引用进来,但是没有使用,那么initialize也不会被调用。 40 | */ 41 | + (void)load{ 42 | static dispatch_once_t onceToken; 43 | dispatch_once(&onceToken, ^{ 44 | memory_cache = [[NSCache alloc] init]; 45 | // 最大成本数,超过会自动清空 46 | memory_cache.totalCostLimit = 10; 47 | 48 | autoCache = YES; 49 | 50 | // 缓存文件路径 51 | [self fl_dishCachePath]; 52 | 53 | // 收到内存警告,要清空缓存 54 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(fl_clearMemoryCache) name:UIApplicationDidReceiveMemoryWarningNotification object:nil]; 55 | 56 | 57 | // 交换方法 58 | [self fl_methodSwizzlingWithOriginalSelector:@selector(reloadData) bySwizzledSelector:@selector(fl_reloadData)]; 59 | 60 | }); 61 | } 62 | 63 | - (void)dealloc{ 64 | [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidReceiveMemoryWarningNotification object:nil]; 65 | } 66 | 67 | + (void)fl_dishCachePath{ 68 | cache = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"fl_cache"]; 69 | BOOL isDir = NO; 70 | BOOL isExists = [[NSFileManager defaultManager] fileExistsAtPath:cache isDirectory:&isDir]; 71 | if (!isExists || !isDir) { 72 | [[NSFileManager defaultManager] createDirectoryAtPath:cache withIntermediateDirectories:YES attributes:nil error:nil]; 73 | } 74 | } 75 | 76 | + (void)fl_methodSwizzlingWithOriginalSelector:(SEL)originalSelector bySwizzledSelector:(SEL)swizzledSelector{ 77 | Class class = [self class]; 78 | Method originalMethod = class_getInstanceMethod(class, originalSelector); 79 | Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector); 80 | BOOL didAddMethod = class_addMethod(class,originalSelector, 81 | method_getImplementation(swizzledMethod), 82 | method_getTypeEncoding(swizzledMethod)); 83 | if (didAddMethod) { 84 | class_replaceMethod(class,swizzledSelector, 85 | method_getImplementation(originalMethod), 86 | method_getTypeEncoding(originalMethod)); 87 | } 88 | else { 89 | method_exchangeImplementations(originalMethod, swizzledMethod); 90 | } 91 | } 92 | 93 | static char *static_no_data_key = "static_no_data_key"; 94 | static char *static_no_network_key = "static_no_network_key"; 95 | static char *static_ImageView_key = "static_ImageView_key"; 96 | static char *static_ImageView_operation_key = "static_ImageView_operation_key"; 97 | static char *static_autoCache_key = "static_autoCache_key"; 98 | 99 | - (void)setFl_noData_image:(NSString *)fl_noData_image{ 100 | NSAssert(fl_noData_image, @"fl_noData_image 不能为空"); 101 | objc_setAssociatedObject(self, &static_no_data_key, fl_noData_image, OBJC_ASSOCIATION_COPY_NONATOMIC); 102 | 103 | } 104 | 105 | - (NSString *)fl_noData_image{ 106 | return objc_getAssociatedObject(self, &static_no_data_key); 107 | } 108 | 109 | - (void)setFl_noNetwork_image:(NSString *)fl_noNetwork_image{ 110 | NSAssert(fl_noNetwork_image, @"fl_noNetwork_image 不能为空"); 111 | objc_setAssociatedObject(self, &static_no_network_key, fl_noNetwork_image, OBJC_ASSOCIATION_COPY_NONATOMIC); 112 | 113 | } 114 | 115 | - (NSString *)fl_noNetwork_image{ 116 | return objc_getAssociatedObject(self, &static_no_network_key); 117 | } 118 | 119 | - (void)setImageView:(UIImageView *)imageView{ 120 | objc_setAssociatedObject(self, &static_ImageView_key, imageView, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 121 | } 122 | 123 | - (UIImageView *)imageView{ 124 | UIImageView *imgV = objc_getAssociatedObject(self, &static_ImageView_key); 125 | if (imgV == nil) { 126 | // 添加到wrapperView,tableView的y不会下移导航栏的高度,wrapperView会偏移 127 | UIView *wrapperView = [self valueForKey:@"wrapperView"]; 128 | imgV = [[UIImageView alloc] init]; 129 | imgV.userInteractionEnabled = YES; 130 | // 添加事件 131 | UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(clickToOperate)]; 132 | [imgV addGestureRecognizer:tap]; 133 | self.imageView = imgV; 134 | // 布局frame 135 | [self updataImageViewFrame]; 136 | 137 | [wrapperView addSubview:imgV]; 138 | [wrapperView bringSubviewToFront:imgV]; 139 | } 140 | return imgV; 141 | } 142 | 143 | - (void)setClickOperation:(void (^)())clickOperation{ 144 | objc_setAssociatedObject(self, &static_ImageView_operation_key, clickOperation, OBJC_ASSOCIATION_COPY_NONATOMIC); 145 | } 146 | 147 | - (void (^)())clickOperation{ 148 | return objc_getAssociatedObject(self, &static_ImageView_operation_key); 149 | } 150 | 151 | - (void)setFl_autoCache:(BOOL)fl_autoCache{ 152 | objc_setAssociatedObject(self, &static_autoCache_key, @(fl_autoCache), OBJC_ASSOCIATION_ASSIGN); 153 | autoCache = fl_autoCache; 154 | } 155 | 156 | - (BOOL)fl_autoCache{ 157 | // NSNumber *autoCacheNum = objc_getAssociatedObject(self, &static_autoCache_key); 158 | // return autoCacheNum.boolValue; 159 | return autoCache; 160 | } 161 | 162 | - (void)clickToOperate{ 163 | if (self.clickOperation) { 164 | self.clickOperation(); 165 | } 166 | } 167 | 168 | - (void)updataImageViewFrame{ 169 | // 如果没有导航控制器,那么rect的y = 0,如果有导航控制器,而且UINavigationBar是显示,那么y = -64,如果有导航控制器,但UINavigationBar隐藏,那么y = -20 170 | Class conecreteValue = NSClassFromString(@"NSConcreteValue"); 171 | id concreteV = [[conecreteValue alloc] init]; 172 | concreteV = [self valueForKey:@"visibleBounds"]; 173 | CGRect rect ; 174 | [concreteV getValue:&rect]; 175 | 176 | // 判断是否有tabBar显示 177 | // 注意:分类中使用[UITabBar appearance] 和 [UINavigationBar appearance] 都不能获取对象,断点po提示<_UIAppearance:0x17025b000> with invocations (null)> 178 | UIViewController *currentVc = [self fl_viewController]; 179 | UITabBarController *tabVc = (UITabBarController *)currentVc.tabBarController; 180 | if (tabVc) { 181 | self.imageView.frame = CGRectMake(rect.origin.x, 0, rect.size.width, rect.size.height + rect.origin.y - (tabVc.tabBar.hidden ? 0 : tabVc.tabBar.bounds.size.height)); 182 | } 183 | else{ 184 | self.imageView.frame = CGRectMake(rect.origin.x, 0, rect.size.width, rect.size.height + rect.origin.y); 185 | } 186 | } 187 | 188 | /** 189 | * @author gitKong 190 | * 191 | * 设置imageView 的 image 192 | */ 193 | - (void)setImage:(NSString *)image{ 194 | 195 | // 更新imageView 的frame 196 | [self updataImageViewFrame]; 197 | 198 | // 判断内存中是否存在 199 | UIImage *memory_cache_image = [memory_cache objectForKey:image]; 200 | if (memory_cache_image) { 201 | self.imageView.image = memory_cache_image; 202 | } 203 | else{ 204 | // 判断沙盒,先把名字/去掉,防止创建多个文件夹 205 | NSString *imageName = [image stringByReplacingOccurrencesOfString:@"/" withString:@""]; 206 | NSString *path = [cache stringByAppendingPathComponent:imageName]; 207 | NSData *data = [NSData dataWithContentsOfFile:path]; 208 | if (data) { 209 | UIImage *dish_cache_image = [self getImageWithData:data]; 210 | self.imageView.image = dish_cache_image; 211 | if (self.fl_autoCache) { 212 | // 缓存到内存中 213 | [memory_cache setObject:dish_cache_image forKey:image]; 214 | } 215 | } 216 | 217 | else{ 218 | NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:image]]; 219 | __weak typeof(self) weakSelf = self; 220 | 221 | if (data) {// 网络url 222 | dispatch_async(dispatch_get_global_queue(0, 0), ^{ 223 | UIImage *networkImg = [self getImageWithData:data]; 224 | 225 | if (weakSelf.fl_autoCache) { 226 | // 缓存到内存中 227 | [memory_cache setObject:networkImg forKey:image]; 228 | // 缓存在沙盒中 229 | [data writeToFile:path atomically:YES]; 230 | } 231 | 232 | dispatch_async(dispatch_get_main_queue(), ^{ 233 | weakSelf.imageView.image = networkImg; 234 | }); 235 | }); 236 | 237 | } 238 | else{// 项目中文件 239 | if ([image rangeOfString:@".gif"].location != NSNotFound) { 240 | UIImage *local_gif_image = [self gifImageNamed:image]; 241 | self.imageView.image = local_gif_image; 242 | if (weakSelf.fl_autoCache) { 243 | // 缓存到内存中 244 | [memory_cache setObject:local_gif_image forKey:image]; 245 | } 246 | } 247 | else{ 248 | self.imageView.image = [UIImage imageNamed:image]; 249 | } 250 | } 251 | } 252 | } 253 | } 254 | 255 | #pragma mark 下载图片,如果是gif则计算动画时长 256 | - (UIImage *)getImageWithData:(NSData *)data{ 257 | CGImageSourceRef imageSource = CGImageSourceCreateWithData((__bridge CFDataRef)data, NULL); 258 | size_t count = CGImageSourceGetCount(imageSource); 259 | if (count <= 1) { //非gif 260 | CFRelease(imageSource); 261 | return [[UIImage alloc] initWithData:data]; 262 | } else { //gif图片 263 | NSMutableArray *images = [NSMutableArray array]; 264 | NSTimeInterval duration = 0; 265 | for (size_t i = 0; i < count; i++) { 266 | CGImageRef image = CGImageSourceCreateImageAtIndex(imageSource, i, NULL); 267 | if (!image) continue; 268 | duration += [self durationWithSource:imageSource atIndex:i]; 269 | [images addObject:[UIImage imageWithCGImage:image]]; 270 | CGImageRelease(image); 271 | } 272 | if (!duration) duration = 0.1 * count; 273 | CFRelease(imageSource); 274 | return [UIImage animatedImageWithImages:images duration:duration]; 275 | } 276 | } 277 | 278 | #pragma mark 获取每一帧图片的时长 279 | - (CGFloat)durationWithSource:(CGImageSourceRef)source atIndex:(NSUInteger)index { 280 | float duration = 0.1f; 281 | CFDictionaryRef propertiesRef = CGImageSourceCopyPropertiesAtIndex(source, index, nil); 282 | NSDictionary *properties = (__bridge NSDictionary *)propertiesRef; 283 | NSDictionary *gifProperties = properties[(NSString *)kCGImagePropertyGIFDictionary]; 284 | 285 | NSNumber *delayTime = gifProperties[(NSString *)kCGImagePropertyGIFUnclampedDelayTime]; 286 | if (delayTime) duration = delayTime.floatValue; 287 | else { 288 | delayTime = gifProperties[(NSString *)kCGImagePropertyGIFDelayTime]; 289 | if (delayTime) duration = delayTime.floatValue; 290 | } 291 | CFRelease(propertiesRef); 292 | return duration; 293 | } 294 | 295 | - (UIImage *)gifImageNamed:(NSString *)gifName{ 296 | 297 | NSString *imagePath = [[NSBundle mainBundle] pathForResource:gifName ofType:nil]; 298 | NSData *data = [NSData dataWithContentsOfFile:imagePath]; 299 | if (data) return [self getImageWithData:data]; 300 | 301 | return [UIImage imageNamed:gifName]; 302 | } 303 | 304 | 305 | 306 | - (BOOL)checkNoNetwork{ 307 | BOOL flag = NO; 308 | UIApplication *app = [UIApplication sharedApplication]; 309 | NSArray *children = [[[app valueForKeyPath:@"statusBar"] valueForKeyPath:@"foregroundView"] subviews]; 310 | int netType = 0; 311 | //获取到网络返回码 312 | for (id child in children) { 313 | if ([child isKindOfClass:NSClassFromString(@"UIStatusBarDataNetworkItemView")]) { 314 | //获取到状态栏,飞行模式和关闭移动网络都拿不到dataNetworkType;1 - 2G; 2 - 3G; 3 - 4G; 5 - WIFI 315 | netType = [[child valueForKeyPath:@"dataNetworkType"] intValue]; 316 | 317 | switch (netType) { 318 | case 0: 319 | flag = NO; 320 | //无网模式 321 | break; 322 | 323 | default: 324 | flag = YES; 325 | break; 326 | } 327 | } 328 | } 329 | return flag; 330 | } 331 | 332 | 333 | - (BOOL)checkNoData{ 334 | NSInteger sections = 1; 335 | NSInteger row = 0; 336 | BOOL isEmpty = YES; 337 | if ([self.dataSource respondsToSelector:@selector(numberOfSectionsInTableView:)]) { 338 | sections = [self.dataSource numberOfSectionsInTableView:self]; 339 | } 340 | for (NSInteger section = 0; section < sections; section++) { 341 | if ([self.dataSource respondsToSelector:@selector(tableView:numberOfRowsInSection:)]) { 342 | row = [self.dataSource tableView:self numberOfRowsInSection:section]; 343 | if (row) { 344 | // 只要有值都不是空 345 | isEmpty = NO; 346 | } 347 | else{ 348 | isEmpty = YES; 349 | } 350 | } 351 | } 352 | return isEmpty; 353 | } 354 | 355 | - (void)fl_reloadData{ 356 | // 判断网络状态 357 | if(![self checkNoNetwork]){ 358 | [self setImage:self.fl_noNetwork_image]; 359 | self.imageView.hidden = NO; 360 | self.separatorStyle = UITableViewCellSeparatorStyleNone; 361 | } 362 | else{ 363 | // 检查数据是否为空 364 | if([self checkNoData]){ 365 | self.imageView.hidden = NO; 366 | [self setImage:self.fl_noData_image]; 367 | self.separatorStyle = UITableViewCellSeparatorStyleNone; 368 | } 369 | else{ 370 | self.imageView.hidden = YES; 371 | self.separatorStyle = UITableViewCellSeparatorStyleSingleLine; 372 | } 373 | } 374 | 375 | [self fl_reloadData]; 376 | } 377 | 378 | 379 | - (void)fl_clearCache{ 380 | [self fl_clearMemoryCache]; 381 | [self fl_clearDiskCache]; 382 | } 383 | 384 | - (void)fl_clearMemoryCache{ 385 | // 清空缓存 386 | [memory_cache removeAllObjects]; 387 | } 388 | 389 | - (void)fl_clearDiskCache { 390 | // 清空沙盒 391 | dispatch_async(dispatch_get_global_queue(0, 0), ^{ 392 | NSArray *contents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:cache error:NULL]; 393 | 394 | for (NSString *fileName in contents) { 395 | [[NSFileManager defaultManager] removeItemAtPath:[cache stringByAppendingPathComponent:fileName] error:nil]; 396 | } 397 | }); 398 | } 399 | 400 | - (void)fl_imageViewClickOperation:(void(^)())clickOperation{ 401 | if (clickOperation) { 402 | self.clickOperation = clickOperation; 403 | } 404 | } 405 | 406 | /** 407 | * @author gitKong 408 | * 409 | * 找到当前的控制器 410 | * 摘自我简书的文章:http://www.jianshu.com/p/dcd26e1ab30f 411 | */ 412 | - (UIViewController *)fl_viewController{ 413 | 414 | UIViewController *vc = [UIApplication sharedApplication].keyWindow.rootViewController; 415 | // modal 416 | if (vc.presentedViewController) { 417 | if ([vc.presentedViewController isKindOfClass:[UINavigationController class]]) { 418 | UINavigationController *navVc = (UINavigationController *)vc.presentedViewController; 419 | vc = navVc.visibleViewController; 420 | } 421 | else if ([vc.presentedViewController isKindOfClass:[UITabBarController class]]){ 422 | UITabBarController *tabVc = (UITabBarController *)vc.presentedViewController; 423 | if ([tabVc.selectedViewController isKindOfClass:[UINavigationController class]]) { 424 | UINavigationController *navVc = (UINavigationController *)tabVc.selectedViewController; 425 | return navVc.visibleViewController; 426 | } 427 | else{ 428 | return tabVc.selectedViewController; 429 | } 430 | } 431 | else{ 432 | vc = vc.presentedViewController; 433 | } 434 | } 435 | // push 436 | else{ 437 | if ([vc isKindOfClass:[UITabBarController class]]) { 438 | UITabBarController *tabVc = (UITabBarController *)vc; 439 | if ([tabVc.selectedViewController isKindOfClass:[UINavigationController class]]) { 440 | UINavigationController *navVc = (UINavigationController *)tabVc.selectedViewController; 441 | return navVc.visibleViewController; 442 | } 443 | else{ 444 | return tabVc.selectedViewController; 445 | } 446 | } 447 | else if([vc isKindOfClass:[UINavigationController class]]){ 448 | UINavigationController *navVc = (UINavigationController *)vc; 449 | vc = navVc.visibleViewController; 450 | } 451 | } 452 | return vc; 453 | } 454 | 455 | @end 456 | -------------------------------------------------------------------------------- /UITableView + NoData/UITableView + NoData/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // UITableView + NoData 4 | // 5 | // Created by clarence on 16/12/5. 6 | // Copyright © 2016年 gitKong. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /UITableView + NoData/UITableView + NoData/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // UITableView + NoData 4 | // 5 | // Created by clarence on 16/12/5. 6 | // Copyright © 2016年 gitKong. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import "UITableView+fl_category.h" 11 | #import "UIImageView+WebCache.h" 12 | @interface ViewController () 13 | @property (nonatomic,strong)NSMutableArray *modelArrM; 14 | @property (nonatomic,weak)UITableView *tableView; 15 | @end 16 | 17 | @implementation ViewController 18 | 19 | - (void)viewDidLoad { 20 | [super viewDidLoad]; 21 | // Do any additional setup after loading the view, typically from a nib. 22 | 23 | 24 | for (NSInteger index = 0; index < 10; index ++) { 25 | [self.modelArrM addObject:@" "]; 26 | } 27 | 28 | 29 | self.title = @"gitKong"; 30 | 31 | self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"hide Nav & Tab" style:UIBarButtonItemStyleDone target:self action:@selector(hide)]; 32 | 33 | self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"clear & reloadData" style:UIBarButtonItemStyleDone target:self action:@selector(reloadModelArrM)]; 34 | 35 | 36 | UITableView *tableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain]; 37 | tableView.dataSource = self; 38 | tableView.delegate = self; 39 | // 没数据显示 40 | // tableView.fl_noData_image = @"https://github.com/fluidicon.png"; 41 | tableView.fl_noData_image = @"http://photo.l99.com/source/11/1330351552722_cxn26e.gif"; 42 | // tableView.fl_noData_image = @"2.gif"; 43 | 44 | // 没网络显示 45 | tableView.fl_noNetwork_image = @"2.gif"; 46 | 47 | // 点击操作 48 | __weak typeof(self) weakSelf = self; 49 | [tableView fl_imageViewClickOperation:^{ 50 | __strong typeof(weakSelf) strongSelf = weakSelf; 51 | strongSelf.navigationController.navigationBarHidden = NO; 52 | strongSelf.tabBarController.tabBar.hidden = NO; 53 | [strongSelf.modelArrM addObjectsFromArray:@[@" ",@"1",@" "]]; 54 | [strongSelf.tableView reloadData]; 55 | NSLog(@"----"); 56 | }]; 57 | [self.view addSubview:tableView]; 58 | self.tableView = tableView; 59 | 60 | 61 | /** 62 | * @author gitKong 63 | * 64 | * 测试发现SDWebImage 加载gif 也是会出现内存飙升 65 | */ 66 | // UIImageView *imageView = [[UIImageView alloc] initWithFrame:self.view.bounds]; 67 | // [imageView sd_setImageWithURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"test.gif" ofType:nil]] placeholderImage:nil]; 68 | // [self.view addSubview:imageView]; 69 | 70 | } 71 | 72 | 73 | 74 | 75 | - (void)hide{ 76 | self.navigationController.navigationBarHidden = YES; 77 | self.tabBarController.tabBar.hidden = YES; 78 | [self.tableView reloadData]; 79 | } 80 | 81 | - (void)reloadModelArrM{ 82 | [self.modelArrM removeAllObjects]; 83 | [self.tableView reloadData]; 84 | } 85 | 86 | 87 | #pragma mark - Table view data source & delegate 88 | static NSString * resueId = @"cell"; 89 | 90 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { 91 | 92 | return 1; 93 | } 94 | 95 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 96 | 97 | return self.modelArrM.count; 98 | } 99 | 100 | 101 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 102 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:resueId]; 103 | if (cell == nil) { 104 | cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:resueId]; 105 | } 106 | cell.textLabel.text = @"gitKong"; 107 | return cell; 108 | } 109 | 110 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{ 111 | self.navigationController.navigationBarHidden = NO; 112 | self.tabBarController.tabBar.hidden = NO; 113 | [self.tableView fl_clearCache]; 114 | NSLog(@"hello gitKong"); 115 | } 116 | 117 | - (NSMutableArray *)modelArrM{ 118 | if (_modelArrM == nil) { 119 | _modelArrM = [NSMutableArray array]; 120 | } 121 | return _modelArrM; 122 | } 123 | 124 | 125 | @end 126 | -------------------------------------------------------------------------------- /UITableView + NoData/UITableView + NoData/fluidicon.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gitkong/UITableView-category/7fa17a5614f7052c148b420da9ac4a668d862fd9/UITableView + NoData/UITableView + NoData/fluidicon.jpeg -------------------------------------------------------------------------------- /UITableView + NoData/UITableView + NoData/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // UITableView + NoData 4 | // 5 | // Created by clarence on 16/12/5. 6 | // Copyright © 2016年 gitKong. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | --------------------------------------------------------------------------------