├── .gitignore ├── Example ├── AppDelegate.h ├── AppDelegate.m ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ ├── Contents.json │ └── warning.imageset │ │ ├── Contents.json │ │ └── warning@2x.png ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── CollectionViewController.h ├── CollectionViewController.m ├── Info.plist ├── TableViewController.h ├── TableViewController.m └── main.m ├── Images ├── image1.jpg └── image2.jpg ├── KLTableViewAndCollectionViewPlaceholder.podspec ├── KLTableViewAndCollectionViewPlaceholder.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── xcshareddata │ └── xcschemes │ └── KLTableViewAndCollectionViewPlaceholder.xcscheme ├── KLTableViewAndCollectionViewPlaceholder ├── Info.plist ├── KLTableViewAndCollectionViewPlaceholder.h ├── KLUtility.h ├── KLUtility.m ├── UICollectionView+KLCollectionViewPlaceholder.h ├── UICollectionView+KLCollectionViewPlaceholder.m ├── UITableView+KLTableViewPlaceholder.h └── UITableView+KLTableViewPlaceholder.m ├── LICENSE └── README.md /.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 | *.xcuserstate 23 | 24 | ## Obj-C/Swift specific 25 | *.hmap 26 | *.ipa 27 | *.dSYM.zip 28 | *.dSYM 29 | 30 | # CocoaPods 31 | # 32 | # We recommend against adding the Pods directory to your .gitignore. However 33 | # you should judge for yourself, the pros and cons are mentioned at: 34 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 35 | # 36 | # Pods/ 37 | 38 | # Carthage 39 | # 40 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 41 | # Carthage/Checkouts 42 | 43 | Carthage/Build 44 | 45 | # fastlane 46 | # 47 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 48 | # screenshots whenever they are needed. 49 | # For more information about the recommended setup visit: 50 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md 51 | 52 | fastlane/report.xml 53 | fastlane/screenshots 54 | 55 | #Code Injection 56 | # 57 | # After new code Injection tools there's a generated folder /iOSInjectionProject 58 | # https://github.com/johnno1962/injectionforxcode 59 | 60 | iOSInjectionProject/ 61 | -------------------------------------------------------------------------------- /Example/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // Example 4 | // 5 | // Created by 柯磊 on 2017/2/24. 6 | // Copyright © 2017年 https://github.com/klaus01 All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | @end 16 | 17 | -------------------------------------------------------------------------------- /Example/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // Example 4 | // 5 | // Created by 柯磊 on 2017/2/24. 6 | // Copyright © 2017年 https://github.com/klaus01 All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @implementation AppDelegate 12 | 13 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 14 | return YES; 15 | } 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /Example/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | } 88 | ], 89 | "info" : { 90 | "version" : 1, 91 | "author" : "xcode" 92 | } 93 | } -------------------------------------------------------------------------------- /Example/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Example/Assets.xcassets/warning.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "warning@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/Assets.xcassets/warning.imageset/warning@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/klaus01/KLTableViewAndCollectionViewPlaceholder/76dcecc926ce150cce54b089ce5d0e5b196639a3/Example/Assets.xcassets/warning.imageset/warning@2x.png -------------------------------------------------------------------------------- /Example/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 | -------------------------------------------------------------------------------- /Example/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 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | -------------------------------------------------------------------------------- /Example/CollectionViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // CollectionViewController.h 3 | // Example 4 | // 5 | // Created by 柯磊 on 2017/2/24. 6 | // Copyright © 2017年 https://github.com/klaus01 All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface CollectionViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Example/CollectionViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // CollectionViewController.m 3 | // Example 4 | // 5 | // Created by 柯磊 on 2017/2/24. 6 | // Copyright © 2017年 https://github.com/klaus01 All rights reserved. 7 | // 8 | 9 | #import "CollectionViewController.h" 10 | #import "KLTableViewAndCollectionViewPlaceholder.h" 11 | 12 | @interface CollectionViewController () 13 | @property (weak, nonatomic) IBOutlet UICollectionView *collectionView; 14 | @property (nonatomic, strong) UIView *placeholderView; 15 | 16 | @property (nonatomic, strong) NSMutableArray *> *dataList; 17 | @end 18 | 19 | @implementation CollectionViewController 20 | 21 | static NSString *const kCellReuseIdentifier = @"MYCELL"; 22 | 23 | - (void)viewDidLoad { 24 | [super viewDidLoad]; 25 | self.dataList = [NSMutableArray array]; 26 | 27 | [self.collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:kCellReuseIdentifier]; 28 | 29 | __weak typeof(self) weakself = self; 30 | [self.collectionView kl_placeholderViewBlock:^UIView * _Nonnull(UICollectionView * _Nonnull collectionView) { 31 | return weakself.placeholderView; 32 | }]; 33 | } 34 | 35 | - (UIView *)placeholderView { 36 | if (!_placeholderView) { 37 | UIImageView *imageView = [[UIImageView alloc] init]; 38 | imageView.translatesAutoresizingMaskIntoConstraints = NO; 39 | imageView.image = [UIImage imageNamed:@"warning"]; 40 | 41 | UIButton *button = [UIButton buttonWithType:UIButtonTypeSystem]; 42 | button.translatesAutoresizingMaskIntoConstraints = NO; 43 | [button setTitle:@"reload" forState:UIControlStateNormal]; 44 | [button addTarget:self action:@selector(reloadButtonAction:) forControlEvents:UIControlEventTouchUpInside]; 45 | 46 | UIView *view = [[UIView alloc] init]; 47 | [view addSubview:imageView]; 48 | [view addSubview:button]; 49 | 50 | [view addConstraint:[NSLayoutConstraint constraintWithItem:imageView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:view attribute:NSLayoutAttributeCenterX multiplier:1 constant:0]]; 51 | [view addConstraint:[NSLayoutConstraint constraintWithItem:imageView attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:view attribute:NSLayoutAttributeCenterY multiplier:1 constant:-25]]; 52 | 53 | [view addConstraint:[NSLayoutConstraint constraintWithItem:button attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:view attribute:NSLayoutAttributeCenterX multiplier:1 constant:0]]; 54 | [view addConstraint:[NSLayoutConstraint constraintWithItem:button attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:imageView attribute:NSLayoutAttributeBottom multiplier:1 constant:0]]; 55 | [view addConstraint:[NSLayoutConstraint constraintWithItem:button attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:44]]; 56 | 57 | _placeholderView = view; 58 | } 59 | return _placeholderView; 60 | } 61 | 62 | #pragma mark - private methods 63 | 64 | - (void)_addData { 65 | NSInteger count = (arc4random() % 5) + 5; 66 | NSMutableArray *colors = [NSMutableArray arrayWithCapacity:count]; 67 | for (NSInteger i = 0; i < count; i++) { 68 | CGFloat r = (arc4random() % 1000) / 1000.0; 69 | CGFloat g = (arc4random() % 1000) / 1000.0; 70 | CGFloat b = (arc4random() % 1000) / 1000.0; 71 | [colors addObject:[UIColor colorWithRed:r green:g blue:b alpha:1.0]]; 72 | } 73 | [self.dataList addObject:colors]; 74 | } 75 | 76 | #pragma mark - actions 77 | 78 | - (IBAction)addAction:(id)sender { 79 | [self _addData]; 80 | [self.collectionView insertSections:[NSIndexSet indexSetWithIndex:self.dataList.count - 1]]; 81 | } 82 | 83 | - (IBAction)add2Action:(id)sender { 84 | [self.collectionView performBatchUpdates:^{ 85 | [self _addData]; 86 | [self.collectionView insertSections:[NSIndexSet indexSetWithIndex:self.dataList.count - 1]]; 87 | [self _addData]; 88 | [self.collectionView insertSections:[NSIndexSet indexSetWithIndex:self.dataList.count - 1]]; 89 | } completion:^(BOOL finished) { 90 | NSLog(@"performBatchUpdates completion"); 91 | }]; 92 | } 93 | 94 | - (IBAction)deleteAction:(id)sender { 95 | NSInteger count = self.dataList.count; 96 | if (count > 0) { 97 | [self.dataList removeLastObject]; 98 | [self.collectionView deleteSections:[NSIndexSet indexSetWithIndex:count - 1]]; 99 | } 100 | } 101 | 102 | - (IBAction)clearAction:(id)sender { 103 | [self.dataList removeAllObjects]; 104 | [self.collectionView reloadData]; 105 | } 106 | 107 | - (IBAction)restoreAction:(id)sender { 108 | [self.collectionView kl_placeholderViewBlock:nil]; 109 | } 110 | 111 | - (void)reloadButtonAction:(id)sender { 112 | [self _addData]; 113 | [self _addData]; 114 | [self.collectionView reloadData]; 115 | } 116 | 117 | #pragma mark - collection view delegate 118 | 119 | - (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView { 120 | return self.dataList.count; 121 | } 122 | 123 | - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { 124 | return self.dataList[section].count; 125 | } 126 | 127 | - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { 128 | UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:kCellReuseIdentifier forIndexPath:indexPath]; 129 | cell.contentView.backgroundColor = self.dataList[indexPath.section][indexPath.item]; 130 | return cell; 131 | } 132 | 133 | @end 134 | -------------------------------------------------------------------------------- /Example/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 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /Example/TableViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // TableViewController.h 3 | // Example 4 | // 5 | // Created by 柯磊 on 2017/2/24. 6 | // Copyright © 2017年 https://github.com/klaus01 All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface TableViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /Example/TableViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // TableViewController.m 3 | // Example 4 | // 5 | // Created by 柯磊 on 2017/2/24. 6 | // Copyright © 2017年 https://github.com/klaus01 All rights reserved. 7 | // 8 | 9 | #import "TableViewController.h" 10 | #import "KLTableViewAndCollectionViewPlaceholder.h" 11 | 12 | @interface TableViewController () 13 | @property (weak, nonatomic) IBOutlet UITableView *tableView; 14 | 15 | @property (nonatomic, strong) NSMutableArray *dataList; 16 | 17 | @end 18 | 19 | @implementation TableViewController 20 | 21 | static NSString *const kCellReuseIdentifier = @"MYCELL"; 22 | 23 | - (void)viewDidLoad { 24 | [super viewDidLoad]; 25 | self.dataList = [NSMutableArray array]; 26 | 27 | [self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:kCellReuseIdentifier]; 28 | 29 | [self.tableView kl_placeholderViewBlock:^UIView * _Nonnull(UITableView * _Nonnull tableView) { 30 | tableView.scrollEnabled = NO; 31 | 32 | UIImageView *imageView = [[UIImageView alloc] init]; 33 | imageView.translatesAutoresizingMaskIntoConstraints = NO; 34 | imageView.image = [UIImage imageNamed:@"warning"]; 35 | 36 | UILabel *label = [[UILabel alloc] init]; 37 | label.translatesAutoresizingMaskIntoConstraints = NO; 38 | label.text = @"no data"; 39 | 40 | UIView *view = [[UIView alloc] init]; 41 | [view addSubview:imageView]; 42 | [view addSubview:label]; 43 | 44 | [view addConstraint:[NSLayoutConstraint constraintWithItem:imageView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:view attribute:NSLayoutAttributeCenterX multiplier:1 constant:0]]; 45 | [view addConstraint:[NSLayoutConstraint constraintWithItem:imageView attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:view attribute:NSLayoutAttributeCenterY multiplier:1 constant:-25]]; 46 | 47 | [view addConstraint:[NSLayoutConstraint constraintWithItem:label attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:view attribute:NSLayoutAttributeCenterX multiplier:1 constant:0]]; 48 | [view addConstraint:[NSLayoutConstraint constraintWithItem:label attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:imageView attribute:NSLayoutAttributeBottom multiplier:1 constant:10]]; 49 | 50 | return view; 51 | } backToNormalBlock:^(UITableView * _Nonnull tableView) { 52 | tableView.scrollEnabled = YES; 53 | }]; 54 | } 55 | 56 | #pragma mark - actions 57 | 58 | - (IBAction)addAction:(id)sender { 59 | NSInteger count = self.dataList.count; 60 | [self.dataList addObject:[NSDate date]]; 61 | [self.tableView insertRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:count inSection:0]] withRowAnimation:UITableViewRowAnimationAutomatic]; 62 | } 63 | 64 | - (IBAction)add2Action:(id)sender { 65 | [self.tableView beginUpdates]; 66 | 67 | [self.dataList addObject:[NSDate date]]; 68 | [self.tableView insertRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:self.dataList.count - 1 inSection:0]] withRowAnimation:UITableViewRowAnimationAutomatic]; 69 | [self.dataList addObject:[NSDate date]]; 70 | [self.tableView insertRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:self.dataList.count - 1 inSection:0]] withRowAnimation:UITableViewRowAnimationAutomatic]; 71 | 72 | [self.tableView endUpdates]; 73 | } 74 | 75 | - (IBAction)deleteAction:(id)sender { 76 | NSInteger count = self.dataList.count; 77 | if (count > 0) { 78 | [self.dataList removeLastObject]; 79 | [self.tableView deleteRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:count - 1 inSection:0]] withRowAnimation:UITableViewRowAnimationAutomatic]; 80 | } 81 | } 82 | 83 | - (IBAction)clearAction:(id)sender { 84 | [self.dataList removeAllObjects]; 85 | [self.tableView reloadData]; 86 | } 87 | 88 | - (IBAction)restoreAction:(id)sender { 89 | [self.tableView kl_placeholderViewBlock:nil]; 90 | } 91 | 92 | #pragma mark - table view delegate 93 | 94 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 95 | return self.dataList.count; 96 | } 97 | 98 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 99 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kCellReuseIdentifier forIndexPath:indexPath]; 100 | cell.textLabel.text = self.dataList[indexPath.row].description; 101 | return cell; 102 | } 103 | 104 | @end 105 | -------------------------------------------------------------------------------- /Example/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // Example 4 | // 5 | // Created by 柯磊 on 2017/2/24. 6 | // Copyright © 2017年 https://github.com/klaus01. 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 | -------------------------------------------------------------------------------- /Images/image1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/klaus01/KLTableViewAndCollectionViewPlaceholder/76dcecc926ce150cce54b089ce5d0e5b196639a3/Images/image1.jpg -------------------------------------------------------------------------------- /Images/image2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/klaus01/KLTableViewAndCollectionViewPlaceholder/76dcecc926ce150cce54b089ce5d0e5b196639a3/Images/image2.jpg -------------------------------------------------------------------------------- /KLTableViewAndCollectionViewPlaceholder.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "KLTableViewAndCollectionViewPlaceholder" 3 | s.version = "0.1.1" 4 | s.summary = "A line of code implements the UITableView and UICollectionView placeholders." 5 | s.homepage = "https://github.com/klaus01/KLTableViewAndCollectionViewPlaceholder" 6 | s.license = { :type => "MIT", :file => "LICENSE" } 7 | s.author = { "柯磊" => "kelei0017@gmail.com" } 8 | s.platform = :ios, "6.0" 9 | s.source = { :git => "https://github.com/klaus01/KLTableViewAndCollectionViewPlaceholder.git", :tag => s.version.to_s } 10 | s.source_files = "KLTableViewAndCollectionViewPlaceholder/*.{h,m}" 11 | s.requires_arc = true 12 | end -------------------------------------------------------------------------------- /KLTableViewAndCollectionViewPlaceholder.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 247DF6401E5FC5BA005E0914 /* KLTableViewAndCollectionViewPlaceholder.h in Headers */ = {isa = PBXBuildFile; fileRef = 247DF63E1E5FC5BA005E0914 /* KLTableViewAndCollectionViewPlaceholder.h */; settings = {ATTRIBUTES = (Public, ); }; }; 11 | 247DF64E1E5FC655005E0914 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 247DF64D1E5FC655005E0914 /* main.m */; }; 12 | 247DF6511E5FC655005E0914 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 247DF6501E5FC655005E0914 /* AppDelegate.m */; }; 13 | 247DF6571E5FC655005E0914 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 247DF6551E5FC655005E0914 /* Main.storyboard */; }; 14 | 247DF6591E5FC655005E0914 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 247DF6581E5FC655005E0914 /* Assets.xcassets */; }; 15 | 247DF65C1E5FC655005E0914 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 247DF65A1E5FC655005E0914 /* LaunchScreen.storyboard */; }; 16 | 247DF6671E5FC724005E0914 /* KLUtility.h in Headers */ = {isa = PBXBuildFile; fileRef = 247DF6611E5FC724005E0914 /* KLUtility.h */; }; 17 | 247DF6681E5FC724005E0914 /* KLUtility.m in Sources */ = {isa = PBXBuildFile; fileRef = 247DF6621E5FC724005E0914 /* KLUtility.m */; }; 18 | 247DF6691E5FC724005E0914 /* KLUtility.m in Sources */ = {isa = PBXBuildFile; fileRef = 247DF6621E5FC724005E0914 /* KLUtility.m */; }; 19 | 247DF66A1E5FC724005E0914 /* UICollectionView+KLCollectionViewPlaceholder.h in Headers */ = {isa = PBXBuildFile; fileRef = 247DF6631E5FC724005E0914 /* UICollectionView+KLCollectionViewPlaceholder.h */; settings = {ATTRIBUTES = (Public, ); }; }; 20 | 247DF66B1E5FC724005E0914 /* UICollectionView+KLCollectionViewPlaceholder.m in Sources */ = {isa = PBXBuildFile; fileRef = 247DF6641E5FC724005E0914 /* UICollectionView+KLCollectionViewPlaceholder.m */; }; 21 | 247DF66C1E5FC724005E0914 /* UICollectionView+KLCollectionViewPlaceholder.m in Sources */ = {isa = PBXBuildFile; fileRef = 247DF6641E5FC724005E0914 /* UICollectionView+KLCollectionViewPlaceholder.m */; }; 22 | 247DF66D1E5FC724005E0914 /* UITableView+KLTableViewPlaceholder.h in Headers */ = {isa = PBXBuildFile; fileRef = 247DF6651E5FC724005E0914 /* UITableView+KLTableViewPlaceholder.h */; settings = {ATTRIBUTES = (Public, ); }; }; 23 | 247DF66E1E5FC724005E0914 /* UITableView+KLTableViewPlaceholder.m in Sources */ = {isa = PBXBuildFile; fileRef = 247DF6661E5FC724005E0914 /* UITableView+KLTableViewPlaceholder.m */; }; 24 | 247DF66F1E5FC724005E0914 /* UITableView+KLTableViewPlaceholder.m in Sources */ = {isa = PBXBuildFile; fileRef = 247DF6661E5FC724005E0914 /* UITableView+KLTableViewPlaceholder.m */; }; 25 | 247DF6741E5FDA41005E0914 /* CollectionViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 247DF6711E5FDA41005E0914 /* CollectionViewController.m */; }; 26 | 247DF6751E5FDA41005E0914 /* TableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 247DF6731E5FDA41005E0914 /* TableViewController.m */; }; 27 | /* End PBXBuildFile section */ 28 | 29 | /* Begin PBXFileReference section */ 30 | 247DF63B1E5FC5BA005E0914 /* KLTableViewAndCollectionViewPlaceholder.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = KLTableViewAndCollectionViewPlaceholder.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 31 | 247DF63E1E5FC5BA005E0914 /* KLTableViewAndCollectionViewPlaceholder.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KLTableViewAndCollectionViewPlaceholder.h; sourceTree = ""; }; 32 | 247DF63F1E5FC5BA005E0914 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 33 | 247DF64A1E5FC655005E0914 /* Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Example.app; sourceTree = BUILT_PRODUCTS_DIR; }; 34 | 247DF64D1E5FC655005E0914 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 35 | 247DF64F1E5FC655005E0914 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 36 | 247DF6501E5FC655005E0914 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 37 | 247DF6561E5FC655005E0914 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 38 | 247DF6581E5FC655005E0914 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 39 | 247DF65B1E5FC655005E0914 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 40 | 247DF65D1E5FC655005E0914 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 41 | 247DF6611E5FC724005E0914 /* KLUtility.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KLUtility.h; sourceTree = ""; }; 42 | 247DF6621E5FC724005E0914 /* KLUtility.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KLUtility.m; sourceTree = ""; }; 43 | 247DF6631E5FC724005E0914 /* UICollectionView+KLCollectionViewPlaceholder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UICollectionView+KLCollectionViewPlaceholder.h"; sourceTree = ""; }; 44 | 247DF6641E5FC724005E0914 /* UICollectionView+KLCollectionViewPlaceholder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UICollectionView+KLCollectionViewPlaceholder.m"; sourceTree = ""; }; 45 | 247DF6651E5FC724005E0914 /* UITableView+KLTableViewPlaceholder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UITableView+KLTableViewPlaceholder.h"; sourceTree = ""; }; 46 | 247DF6661E5FC724005E0914 /* UITableView+KLTableViewPlaceholder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UITableView+KLTableViewPlaceholder.m"; sourceTree = ""; }; 47 | 247DF6701E5FDA41005E0914 /* CollectionViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CollectionViewController.h; sourceTree = ""; }; 48 | 247DF6711E5FDA41005E0914 /* CollectionViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CollectionViewController.m; sourceTree = ""; }; 49 | 247DF6721E5FDA41005E0914 /* TableViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TableViewController.h; sourceTree = ""; }; 50 | 247DF6731E5FDA41005E0914 /* TableViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TableViewController.m; sourceTree = ""; }; 51 | /* End PBXFileReference section */ 52 | 53 | /* Begin PBXFrameworksBuildPhase section */ 54 | 247DF6371E5FC5BA005E0914 /* Frameworks */ = { 55 | isa = PBXFrameworksBuildPhase; 56 | buildActionMask = 2147483647; 57 | files = ( 58 | ); 59 | runOnlyForDeploymentPostprocessing = 0; 60 | }; 61 | 247DF6471E5FC655005E0914 /* Frameworks */ = { 62 | isa = PBXFrameworksBuildPhase; 63 | buildActionMask = 2147483647; 64 | files = ( 65 | ); 66 | runOnlyForDeploymentPostprocessing = 0; 67 | }; 68 | /* End PBXFrameworksBuildPhase section */ 69 | 70 | /* Begin PBXGroup section */ 71 | 247DF6311E5FC5BA005E0914 = { 72 | isa = PBXGroup; 73 | children = ( 74 | 247DF63D1E5FC5BA005E0914 /* KLTableViewAndCollectionViewPlaceholder */, 75 | 247DF64B1E5FC655005E0914 /* Example */, 76 | 247DF63C1E5FC5BA005E0914 /* Products */, 77 | ); 78 | sourceTree = ""; 79 | }; 80 | 247DF63C1E5FC5BA005E0914 /* Products */ = { 81 | isa = PBXGroup; 82 | children = ( 83 | 247DF63B1E5FC5BA005E0914 /* KLTableViewAndCollectionViewPlaceholder.framework */, 84 | 247DF64A1E5FC655005E0914 /* Example.app */, 85 | ); 86 | name = Products; 87 | sourceTree = ""; 88 | }; 89 | 247DF63D1E5FC5BA005E0914 /* KLTableViewAndCollectionViewPlaceholder */ = { 90 | isa = PBXGroup; 91 | children = ( 92 | 247DF63E1E5FC5BA005E0914 /* KLTableViewAndCollectionViewPlaceholder.h */, 93 | 247DF6611E5FC724005E0914 /* KLUtility.h */, 94 | 247DF6621E5FC724005E0914 /* KLUtility.m */, 95 | 247DF6631E5FC724005E0914 /* UICollectionView+KLCollectionViewPlaceholder.h */, 96 | 247DF6641E5FC724005E0914 /* UICollectionView+KLCollectionViewPlaceholder.m */, 97 | 247DF6651E5FC724005E0914 /* UITableView+KLTableViewPlaceholder.h */, 98 | 247DF6661E5FC724005E0914 /* UITableView+KLTableViewPlaceholder.m */, 99 | 247DF63F1E5FC5BA005E0914 /* Info.plist */, 100 | ); 101 | path = KLTableViewAndCollectionViewPlaceholder; 102 | sourceTree = ""; 103 | }; 104 | 247DF64B1E5FC655005E0914 /* Example */ = { 105 | isa = PBXGroup; 106 | children = ( 107 | 247DF64F1E5FC655005E0914 /* AppDelegate.h */, 108 | 247DF6501E5FC655005E0914 /* AppDelegate.m */, 109 | 247DF6701E5FDA41005E0914 /* CollectionViewController.h */, 110 | 247DF6711E5FDA41005E0914 /* CollectionViewController.m */, 111 | 247DF6721E5FDA41005E0914 /* TableViewController.h */, 112 | 247DF6731E5FDA41005E0914 /* TableViewController.m */, 113 | 247DF6551E5FC655005E0914 /* Main.storyboard */, 114 | 247DF6581E5FC655005E0914 /* Assets.xcassets */, 115 | 247DF65A1E5FC655005E0914 /* LaunchScreen.storyboard */, 116 | 247DF65D1E5FC655005E0914 /* Info.plist */, 117 | 247DF64C1E5FC655005E0914 /* Supporting Files */, 118 | ); 119 | path = Example; 120 | sourceTree = ""; 121 | }; 122 | 247DF64C1E5FC655005E0914 /* Supporting Files */ = { 123 | isa = PBXGroup; 124 | children = ( 125 | 247DF64D1E5FC655005E0914 /* main.m */, 126 | ); 127 | name = "Supporting Files"; 128 | sourceTree = ""; 129 | }; 130 | /* End PBXGroup section */ 131 | 132 | /* Begin PBXHeadersBuildPhase section */ 133 | 247DF6381E5FC5BA005E0914 /* Headers */ = { 134 | isa = PBXHeadersBuildPhase; 135 | buildActionMask = 2147483647; 136 | files = ( 137 | 247DF6671E5FC724005E0914 /* KLUtility.h in Headers */, 138 | 247DF66A1E5FC724005E0914 /* UICollectionView+KLCollectionViewPlaceholder.h in Headers */, 139 | 247DF66D1E5FC724005E0914 /* UITableView+KLTableViewPlaceholder.h in Headers */, 140 | 247DF6401E5FC5BA005E0914 /* KLTableViewAndCollectionViewPlaceholder.h in Headers */, 141 | ); 142 | runOnlyForDeploymentPostprocessing = 0; 143 | }; 144 | /* End PBXHeadersBuildPhase section */ 145 | 146 | /* Begin PBXNativeTarget section */ 147 | 247DF63A1E5FC5BA005E0914 /* KLTableViewAndCollectionViewPlaceholder */ = { 148 | isa = PBXNativeTarget; 149 | buildConfigurationList = 247DF6431E5FC5BA005E0914 /* Build configuration list for PBXNativeTarget "KLTableViewAndCollectionViewPlaceholder" */; 150 | buildPhases = ( 151 | 247DF6361E5FC5BA005E0914 /* Sources */, 152 | 247DF6371E5FC5BA005E0914 /* Frameworks */, 153 | 247DF6381E5FC5BA005E0914 /* Headers */, 154 | 247DF6391E5FC5BA005E0914 /* Resources */, 155 | ); 156 | buildRules = ( 157 | ); 158 | dependencies = ( 159 | ); 160 | name = KLTableViewAndCollectionViewPlaceholder; 161 | productName = KLTableViewAndCollectionViewPlaceholder; 162 | productReference = 247DF63B1E5FC5BA005E0914 /* KLTableViewAndCollectionViewPlaceholder.framework */; 163 | productType = "com.apple.product-type.framework"; 164 | }; 165 | 247DF6491E5FC655005E0914 /* Example */ = { 166 | isa = PBXNativeTarget; 167 | buildConfigurationList = 247DF6601E5FC655005E0914 /* Build configuration list for PBXNativeTarget "Example" */; 168 | buildPhases = ( 169 | 247DF6461E5FC655005E0914 /* Sources */, 170 | 247DF6471E5FC655005E0914 /* Frameworks */, 171 | 247DF6481E5FC655005E0914 /* Resources */, 172 | ); 173 | buildRules = ( 174 | ); 175 | dependencies = ( 176 | ); 177 | name = Example; 178 | productName = Example; 179 | productReference = 247DF64A1E5FC655005E0914 /* Example.app */; 180 | productType = "com.apple.product-type.application"; 181 | }; 182 | /* End PBXNativeTarget section */ 183 | 184 | /* Begin PBXProject section */ 185 | 247DF6321E5FC5BA005E0914 /* Project object */ = { 186 | isa = PBXProject; 187 | attributes = { 188 | LastUpgradeCheck = 0820; 189 | ORGANIZATIONNAME = "https://github.com/klaus01"; 190 | TargetAttributes = { 191 | 247DF63A1E5FC5BA005E0914 = { 192 | CreatedOnToolsVersion = 8.2; 193 | ProvisioningStyle = Automatic; 194 | }; 195 | 247DF6491E5FC655005E0914 = { 196 | CreatedOnToolsVersion = 8.2; 197 | ProvisioningStyle = Automatic; 198 | }; 199 | }; 200 | }; 201 | buildConfigurationList = 247DF6351E5FC5BA005E0914 /* Build configuration list for PBXProject "KLTableViewAndCollectionViewPlaceholder" */; 202 | compatibilityVersion = "Xcode 3.2"; 203 | developmentRegion = English; 204 | hasScannedForEncodings = 0; 205 | knownRegions = ( 206 | en, 207 | Base, 208 | ); 209 | mainGroup = 247DF6311E5FC5BA005E0914; 210 | productRefGroup = 247DF63C1E5FC5BA005E0914 /* Products */; 211 | projectDirPath = ""; 212 | projectRoot = ""; 213 | targets = ( 214 | 247DF63A1E5FC5BA005E0914 /* KLTableViewAndCollectionViewPlaceholder */, 215 | 247DF6491E5FC655005E0914 /* Example */, 216 | ); 217 | }; 218 | /* End PBXProject section */ 219 | 220 | /* Begin PBXResourcesBuildPhase section */ 221 | 247DF6391E5FC5BA005E0914 /* Resources */ = { 222 | isa = PBXResourcesBuildPhase; 223 | buildActionMask = 2147483647; 224 | files = ( 225 | ); 226 | runOnlyForDeploymentPostprocessing = 0; 227 | }; 228 | 247DF6481E5FC655005E0914 /* Resources */ = { 229 | isa = PBXResourcesBuildPhase; 230 | buildActionMask = 2147483647; 231 | files = ( 232 | 247DF65C1E5FC655005E0914 /* LaunchScreen.storyboard in Resources */, 233 | 247DF6591E5FC655005E0914 /* Assets.xcassets in Resources */, 234 | 247DF6571E5FC655005E0914 /* Main.storyboard in Resources */, 235 | ); 236 | runOnlyForDeploymentPostprocessing = 0; 237 | }; 238 | /* End PBXResourcesBuildPhase section */ 239 | 240 | /* Begin PBXSourcesBuildPhase section */ 241 | 247DF6361E5FC5BA005E0914 /* Sources */ = { 242 | isa = PBXSourcesBuildPhase; 243 | buildActionMask = 2147483647; 244 | files = ( 245 | 247DF66B1E5FC724005E0914 /* UICollectionView+KLCollectionViewPlaceholder.m in Sources */, 246 | 247DF66E1E5FC724005E0914 /* UITableView+KLTableViewPlaceholder.m in Sources */, 247 | 247DF6681E5FC724005E0914 /* KLUtility.m in Sources */, 248 | ); 249 | runOnlyForDeploymentPostprocessing = 0; 250 | }; 251 | 247DF6461E5FC655005E0914 /* Sources */ = { 252 | isa = PBXSourcesBuildPhase; 253 | buildActionMask = 2147483647; 254 | files = ( 255 | 247DF6691E5FC724005E0914 /* KLUtility.m in Sources */, 256 | 247DF66C1E5FC724005E0914 /* UICollectionView+KLCollectionViewPlaceholder.m in Sources */, 257 | 247DF6741E5FDA41005E0914 /* CollectionViewController.m in Sources */, 258 | 247DF6511E5FC655005E0914 /* AppDelegate.m in Sources */, 259 | 247DF66F1E5FC724005E0914 /* UITableView+KLTableViewPlaceholder.m in Sources */, 260 | 247DF6751E5FDA41005E0914 /* TableViewController.m in Sources */, 261 | 247DF64E1E5FC655005E0914 /* main.m in Sources */, 262 | ); 263 | runOnlyForDeploymentPostprocessing = 0; 264 | }; 265 | /* End PBXSourcesBuildPhase section */ 266 | 267 | /* Begin PBXVariantGroup section */ 268 | 247DF6551E5FC655005E0914 /* Main.storyboard */ = { 269 | isa = PBXVariantGroup; 270 | children = ( 271 | 247DF6561E5FC655005E0914 /* Base */, 272 | ); 273 | name = Main.storyboard; 274 | sourceTree = ""; 275 | }; 276 | 247DF65A1E5FC655005E0914 /* LaunchScreen.storyboard */ = { 277 | isa = PBXVariantGroup; 278 | children = ( 279 | 247DF65B1E5FC655005E0914 /* Base */, 280 | ); 281 | name = LaunchScreen.storyboard; 282 | sourceTree = ""; 283 | }; 284 | /* End PBXVariantGroup section */ 285 | 286 | /* Begin XCBuildConfiguration section */ 287 | 247DF6411E5FC5BA005E0914 /* Debug */ = { 288 | isa = XCBuildConfiguration; 289 | buildSettings = { 290 | ALWAYS_SEARCH_USER_PATHS = NO; 291 | CLANG_ANALYZER_NONNULL = YES; 292 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 293 | CLANG_CXX_LIBRARY = "libc++"; 294 | CLANG_ENABLE_MODULES = YES; 295 | CLANG_ENABLE_OBJC_ARC = YES; 296 | CLANG_WARN_BOOL_CONVERSION = YES; 297 | CLANG_WARN_CONSTANT_CONVERSION = YES; 298 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 299 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 300 | CLANG_WARN_EMPTY_BODY = YES; 301 | CLANG_WARN_ENUM_CONVERSION = YES; 302 | CLANG_WARN_INFINITE_RECURSION = YES; 303 | CLANG_WARN_INT_CONVERSION = YES; 304 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 305 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 306 | CLANG_WARN_UNREACHABLE_CODE = YES; 307 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 308 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 309 | COPY_PHASE_STRIP = NO; 310 | CURRENT_PROJECT_VERSION = 1; 311 | DEBUG_INFORMATION_FORMAT = dwarf; 312 | ENABLE_STRICT_OBJC_MSGSEND = YES; 313 | ENABLE_TESTABILITY = YES; 314 | GCC_C_LANGUAGE_STANDARD = gnu99; 315 | GCC_DYNAMIC_NO_PIC = NO; 316 | GCC_NO_COMMON_BLOCKS = YES; 317 | GCC_OPTIMIZATION_LEVEL = 0; 318 | GCC_PREPROCESSOR_DEFINITIONS = ( 319 | "DEBUG=1", 320 | "$(inherited)", 321 | ); 322 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 323 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 324 | GCC_WARN_UNDECLARED_SELECTOR = YES; 325 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 326 | GCC_WARN_UNUSED_FUNCTION = YES; 327 | GCC_WARN_UNUSED_VARIABLE = YES; 328 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 329 | MTL_ENABLE_DEBUG_INFO = YES; 330 | ONLY_ACTIVE_ARCH = YES; 331 | SDKROOT = iphoneos; 332 | TARGETED_DEVICE_FAMILY = "1,2"; 333 | VERSIONING_SYSTEM = "apple-generic"; 334 | VERSION_INFO_PREFIX = ""; 335 | }; 336 | name = Debug; 337 | }; 338 | 247DF6421E5FC5BA005E0914 /* Release */ = { 339 | isa = XCBuildConfiguration; 340 | buildSettings = { 341 | ALWAYS_SEARCH_USER_PATHS = NO; 342 | CLANG_ANALYZER_NONNULL = YES; 343 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 344 | CLANG_CXX_LIBRARY = "libc++"; 345 | CLANG_ENABLE_MODULES = YES; 346 | CLANG_ENABLE_OBJC_ARC = YES; 347 | CLANG_WARN_BOOL_CONVERSION = YES; 348 | CLANG_WARN_CONSTANT_CONVERSION = YES; 349 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 350 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 351 | CLANG_WARN_EMPTY_BODY = YES; 352 | CLANG_WARN_ENUM_CONVERSION = YES; 353 | CLANG_WARN_INFINITE_RECURSION = YES; 354 | CLANG_WARN_INT_CONVERSION = YES; 355 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 356 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 357 | CLANG_WARN_UNREACHABLE_CODE = YES; 358 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 359 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 360 | COPY_PHASE_STRIP = NO; 361 | CURRENT_PROJECT_VERSION = 1; 362 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 363 | ENABLE_NS_ASSERTIONS = NO; 364 | ENABLE_STRICT_OBJC_MSGSEND = YES; 365 | GCC_C_LANGUAGE_STANDARD = gnu99; 366 | GCC_NO_COMMON_BLOCKS = YES; 367 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 368 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 369 | GCC_WARN_UNDECLARED_SELECTOR = YES; 370 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 371 | GCC_WARN_UNUSED_FUNCTION = YES; 372 | GCC_WARN_UNUSED_VARIABLE = YES; 373 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 374 | MTL_ENABLE_DEBUG_INFO = NO; 375 | SDKROOT = iphoneos; 376 | TARGETED_DEVICE_FAMILY = "1,2"; 377 | VALIDATE_PRODUCT = YES; 378 | VERSIONING_SYSTEM = "apple-generic"; 379 | VERSION_INFO_PREFIX = ""; 380 | }; 381 | name = Release; 382 | }; 383 | 247DF6441E5FC5BA005E0914 /* Debug */ = { 384 | isa = XCBuildConfiguration; 385 | buildSettings = { 386 | CODE_SIGN_IDENTITY = ""; 387 | DEFINES_MODULE = YES; 388 | DYLIB_COMPATIBILITY_VERSION = 1; 389 | DYLIB_CURRENT_VERSION = 1; 390 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 391 | INFOPLIST_FILE = KLTableViewAndCollectionViewPlaceholder/Info.plist; 392 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 393 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 394 | PRODUCT_BUNDLE_IDENTIFIER = com.kelei.KLTableViewAndCollectionViewPlaceholder; 395 | PRODUCT_NAME = "$(TARGET_NAME)"; 396 | SKIP_INSTALL = YES; 397 | }; 398 | name = Debug; 399 | }; 400 | 247DF6451E5FC5BA005E0914 /* Release */ = { 401 | isa = XCBuildConfiguration; 402 | buildSettings = { 403 | CODE_SIGN_IDENTITY = ""; 404 | DEFINES_MODULE = YES; 405 | DYLIB_COMPATIBILITY_VERSION = 1; 406 | DYLIB_CURRENT_VERSION = 1; 407 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 408 | INFOPLIST_FILE = KLTableViewAndCollectionViewPlaceholder/Info.plist; 409 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 410 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 411 | PRODUCT_BUNDLE_IDENTIFIER = com.kelei.KLTableViewAndCollectionViewPlaceholder; 412 | PRODUCT_NAME = "$(TARGET_NAME)"; 413 | SKIP_INSTALL = YES; 414 | }; 415 | name = Release; 416 | }; 417 | 247DF65E1E5FC655005E0914 /* Debug */ = { 418 | isa = XCBuildConfiguration; 419 | buildSettings = { 420 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 421 | INFOPLIST_FILE = Example/Info.plist; 422 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 423 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 424 | PRODUCT_BUNDLE_IDENTIFIER = com.kelei.KLTableViewAndCollectionViewPlaceholder.Example; 425 | PRODUCT_NAME = "$(TARGET_NAME)"; 426 | }; 427 | name = Debug; 428 | }; 429 | 247DF65F1E5FC655005E0914 /* Release */ = { 430 | isa = XCBuildConfiguration; 431 | buildSettings = { 432 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 433 | INFOPLIST_FILE = Example/Info.plist; 434 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 435 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 436 | PRODUCT_BUNDLE_IDENTIFIER = com.kelei.KLTableViewAndCollectionViewPlaceholder.Example; 437 | PRODUCT_NAME = "$(TARGET_NAME)"; 438 | }; 439 | name = Release; 440 | }; 441 | /* End XCBuildConfiguration section */ 442 | 443 | /* Begin XCConfigurationList section */ 444 | 247DF6351E5FC5BA005E0914 /* Build configuration list for PBXProject "KLTableViewAndCollectionViewPlaceholder" */ = { 445 | isa = XCConfigurationList; 446 | buildConfigurations = ( 447 | 247DF6411E5FC5BA005E0914 /* Debug */, 448 | 247DF6421E5FC5BA005E0914 /* Release */, 449 | ); 450 | defaultConfigurationIsVisible = 0; 451 | defaultConfigurationName = Release; 452 | }; 453 | 247DF6431E5FC5BA005E0914 /* Build configuration list for PBXNativeTarget "KLTableViewAndCollectionViewPlaceholder" */ = { 454 | isa = XCConfigurationList; 455 | buildConfigurations = ( 456 | 247DF6441E5FC5BA005E0914 /* Debug */, 457 | 247DF6451E5FC5BA005E0914 /* Release */, 458 | ); 459 | defaultConfigurationIsVisible = 0; 460 | defaultConfigurationName = Release; 461 | }; 462 | 247DF6601E5FC655005E0914 /* Build configuration list for PBXNativeTarget "Example" */ = { 463 | isa = XCConfigurationList; 464 | buildConfigurations = ( 465 | 247DF65E1E5FC655005E0914 /* Debug */, 466 | 247DF65F1E5FC655005E0914 /* Release */, 467 | ); 468 | defaultConfigurationIsVisible = 0; 469 | defaultConfigurationName = Release; 470 | }; 471 | /* End XCConfigurationList section */ 472 | }; 473 | rootObject = 247DF6321E5FC5BA005E0914 /* Project object */; 474 | } 475 | -------------------------------------------------------------------------------- /KLTableViewAndCollectionViewPlaceholder.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /KLTableViewAndCollectionViewPlaceholder.xcodeproj/xcshareddata/xcschemes/KLTableViewAndCollectionViewPlaceholder.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | 46 | 52 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 70 | 71 | 72 | 73 | 75 | 76 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /KLTableViewAndCollectionViewPlaceholder/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 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /KLTableViewAndCollectionViewPlaceholder/KLTableViewAndCollectionViewPlaceholder.h: -------------------------------------------------------------------------------- 1 | // 2 | // KLTableViewAndCollectionViewPlaceholder.h 3 | // KLTableViewAndCollectionViewPlaceholder 4 | // 5 | // Created by 柯磊 on 2017/2/24. 6 | // Copyright © 2017年 https://github.com/klaus01 All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for KLTableViewAndCollectionViewPlaceholder. 12 | FOUNDATION_EXPORT double KLTableViewAndCollectionViewPlaceholderVersionNumber; 13 | 14 | //! Project version string for KLTableViewAndCollectionViewPlaceholder. 15 | FOUNDATION_EXPORT const unsigned char KLTableViewAndCollectionViewPlaceholderVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | #import 20 | #import 21 | 22 | -------------------------------------------------------------------------------- /KLTableViewAndCollectionViewPlaceholder/KLUtility.h: -------------------------------------------------------------------------------- 1 | // 2 | // KLUtility.h 3 | // KLTableViewAndCollectionViewPlaceholder 4 | // 5 | // Created by 柯磊 on 2017/2/24. 6 | // Copyright © 2017年 https://github.com/klaus01 All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | static NSString *const kNewClassPrefix = @"_KLPlaceholder_"; 12 | 13 | extern void swizzleMethod(Class c, SEL original, SEL alternative); 14 | 15 | -------------------------------------------------------------------------------- /KLTableViewAndCollectionViewPlaceholder/KLUtility.m: -------------------------------------------------------------------------------- 1 | // 2 | // KLUtility.m 3 | // KLTableViewAndCollectionViewPlaceholder 4 | // 5 | // Created by 柯磊 on 2017/2/24. 6 | // Copyright © 2017年 https://github.com/klaus01 All rights reserved. 7 | // 8 | 9 | #import "KLUtility.h" 10 | #import 11 | 12 | void swizzleMethod(Class c, SEL original, SEL alternative) { 13 | Method orgMethod = class_getInstanceMethod(c, original); 14 | Method altMethod = class_getInstanceMethod(c, alternative); 15 | 16 | if (class_addMethod(c, original, method_getImplementation(altMethod), method_getTypeEncoding(altMethod))) { 17 | class_replaceMethod(c, alternative, method_getImplementation(orgMethod), method_getTypeEncoding(orgMethod)); 18 | } else { 19 | method_exchangeImplementations(orgMethod, altMethod); 20 | } 21 | } 22 | 23 | -------------------------------------------------------------------------------- /KLTableViewAndCollectionViewPlaceholder/UICollectionView+KLCollectionViewPlaceholder.h: -------------------------------------------------------------------------------- 1 | // 2 | // UICollectionView+KLCollectionViewPlaceholder.h 3 | // KLTableViewAndCollectionViewPlaceholder 4 | // 5 | // Created by 柯磊 on 2017/2/24. 6 | // Copyright © 2017年 https://github.com/klaus01 All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef UIView * __nullable (^KLCollectionViewPlaceholderViewBlock)(UICollectionView * __nonnull collectionView); 12 | typedef void (^KLCollectionViewBackToNormalBlock)(UICollectionView * __nonnull collectionView); 13 | 14 | @interface UICollectionView (KLCollectionViewPlaceholder) 15 | 16 | /** 17 | Call the placeholderViewBlock when the data is empty. 18 | 19 | placeholderViewBlock = nil is disabled, otherwise the null data display placeholder function is enabled. 20 | 21 | @param placeholderViewBlock Called when a placeholder needs to be displayed, returns a view. 22 | */ 23 | - (void)kl_placeholderViewBlock:(KLCollectionViewPlaceholderViewBlock __nullable)placeholderViewBlock; 24 | 25 | /** 26 | Called placeholderViewBlock when the data is empty, and backToNormalBlock when the data is not empty. 27 | 28 | placeholderViewBlock = nil is disabled, otherwise the null data display placeholder function is enabled. 29 | 30 | @param placeholderViewBlock Called when a placeholder needs to be displayed, returns a view. 31 | @param backToNormalBlock Called when there is data 32 | */ 33 | - (void)kl_placeholderViewBlock:(KLCollectionViewPlaceholderViewBlock __nullable)placeholderViewBlock 34 | backToNormalBlock:(KLCollectionViewBackToNormalBlock __nullable)backToNormalBlock; 35 | 36 | @end 37 | -------------------------------------------------------------------------------- /KLTableViewAndCollectionViewPlaceholder/UICollectionView+KLCollectionViewPlaceholder.m: -------------------------------------------------------------------------------- 1 | // 2 | // UICollectionView+KLCollectionViewPlaceholder.m 3 | // KLTableViewAndCollectionViewPlaceholder 4 | // 5 | // Created by 柯磊 on 2017/2/24. 6 | // Copyright © 2017年 https://github.com/klaus01 All rights reserved. 7 | // 8 | 9 | #import "UICollectionView+KLCollectionViewPlaceholder.h" 10 | #import "KLUtility.h" 11 | #import 12 | 13 | @implementation UICollectionView (KLCollectionViewPlaceholder) 14 | 15 | + (void)load { 16 | swizzleMethod(self, @selector(reloadData), @selector(kl_reloadData)); 17 | swizzleMethod(self, @selector(insertSections:), @selector(kl_insertSections:)); 18 | swizzleMethod(self, @selector(deleteSections:), @selector(kl_deleteSections:)); 19 | swizzleMethod(self, @selector(reloadSections:), @selector(kl_reloadSections:)); 20 | swizzleMethod(self, @selector(insertItemsAtIndexPaths:), @selector(kl_insertItemsAtIndexPaths:)); 21 | swizzleMethod(self, @selector(deleteItemsAtIndexPaths:), @selector(kl_deleteItemsAtIndexPaths:)); 22 | } 23 | 24 | #pragma mark - public methods 25 | 26 | - (void)kl_placeholderViewBlock:(KLCollectionViewPlaceholderViewBlock _Nullable)placeholderViewBlock { 27 | [self kl_placeholderViewBlock:placeholderViewBlock backToNormalBlock:nil]; 28 | } 29 | 30 | - (void)kl_placeholderViewBlock:(KLCollectionViewPlaceholderViewBlock _Nullable)placeholderViewBlock 31 | backToNormalBlock:(KLCollectionViewBackToNormalBlock _Nullable)backToNormalBlock { 32 | self.kl_placeholderViewBlock = placeholderViewBlock; 33 | self.kl_backToNormalBlock = backToNormalBlock; 34 | } 35 | 36 | #pragma mark - property methods 37 | 38 | - (KLCollectionViewPlaceholderViewBlock)kl_placeholderViewBlock { 39 | return objc_getAssociatedObject(self, @selector(kl_placeholderViewBlock)); 40 | } 41 | 42 | - (void)setKl_placeholderViewBlock:(KLCollectionViewPlaceholderViewBlock)kl_placeholderViewBlock { 43 | objc_setAssociatedObject(self, @selector(kl_placeholderViewBlock), kl_placeholderViewBlock, OBJC_ASSOCIATION_COPY_NONATOMIC); 44 | 45 | if (kl_placeholderViewBlock == nil) { 46 | [self kl_removePlaceholderView]; 47 | } 48 | } 49 | 50 | - (KLCollectionViewBackToNormalBlock)kl_backToNormalBlock { 51 | return objc_getAssociatedObject(self, @selector(kl_backToNormalBlock)); 52 | } 53 | 54 | - (void)setKl_backToNormalBlock:(KLCollectionViewBackToNormalBlock)kl_backToNormalBlock { 55 | objc_setAssociatedObject(self, @selector(kl_backToNormalBlock), kl_backToNormalBlock, OBJC_ASSOCIATION_COPY_NONATOMIC); 56 | } 57 | 58 | - (UIView *)kl_placeholderView { 59 | return objc_getAssociatedObject(self, @selector(kl_placeholderView)); 60 | } 61 | 62 | - (void)setKl_placeholderView:(UIView *)kl_placeholderView { 63 | objc_setAssociatedObject(self, @selector(kl_placeholderView), kl_placeholderView, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 64 | } 65 | 66 | - (BOOL)kl_isBatchUpdates { 67 | NSNumber *number = objc_getAssociatedObject(self, @selector(kl_isBatchUpdates)); 68 | return number ? number.boolValue : NO; 69 | } 70 | 71 | - (void)setKl_isBatchUpdates:(BOOL)kl_isBatchUpdates { 72 | objc_setAssociatedObject(self, @selector(kl_isBatchUpdates), @(kl_isBatchUpdates), OBJC_ASSOCIATION_RETAIN_NONATOMIC); 73 | } 74 | 75 | #pragma mark - private methods 76 | 77 | - (void)kl_removePlaceholderView { 78 | if (self.kl_placeholderView) { 79 | [self.kl_placeholderView removeFromSuperview]; 80 | self.kl_placeholderView = nil; 81 | self.kl_backToNormalBlock ? self.kl_backToNormalBlock(self) : nil; 82 | } 83 | } 84 | 85 | - (void)kl_checkEmpty { 86 | dispatch_async(dispatch_get_main_queue(), ^{ 87 | if (self.kl_isBatchUpdates || self.kl_placeholderViewBlock == nil) { 88 | return; 89 | } 90 | 91 | BOOL isEmpty = YES; 92 | 93 | id dataSource = self.dataSource; 94 | NSInteger sections = 1; 95 | if ([dataSource respondsToSelector:@selector(numberOfSectionsInCollectionView:)]) { 96 | sections = [dataSource numberOfSectionsInCollectionView:self]; 97 | } 98 | 99 | for (NSInteger i = 0; i < sections; i++) { 100 | NSInteger rows = [dataSource collectionView:self numberOfItemsInSection:i]; 101 | if (rows) { 102 | isEmpty = NO; 103 | break; 104 | } 105 | } 106 | 107 | if (isEmpty) { 108 | UIView *placeholderView = self.kl_placeholderViewBlock ? self.kl_placeholderViewBlock(self) : nil; 109 | if (self.kl_placeholderView != placeholderView) { 110 | [self.kl_placeholderView removeFromSuperview]; 111 | self.kl_placeholderView = placeholderView; 112 | if (placeholderView) { 113 | placeholderView.translatesAutoresizingMaskIntoConstraints = NO; 114 | [self addSubview:placeholderView]; 115 | NSDictionary *views = @{@"view": placeholderView, @"superview": self}; 116 | [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[view(==superview)]|" options:0 metrics:nil views:views]]; 117 | [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[view(==superview)]|" options:0 metrics:nil views:views]]; 118 | } 119 | } 120 | } else { 121 | [self kl_removePlaceholderView]; 122 | } 123 | }); 124 | } 125 | 126 | #pragma mark - hook methods 127 | 128 | - (void)kl_reloadData { 129 | [self kl_reloadData]; 130 | [self kl_checkEmpty]; 131 | } 132 | 133 | - (void)kl_insertSections:(NSIndexSet *)sections { 134 | [self kl_insertSections:sections]; 135 | [self kl_checkEmpty]; 136 | } 137 | 138 | - (void)kl_deleteSections:(NSIndexSet *)sections { 139 | [self kl_deleteSections:sections]; 140 | [self kl_checkEmpty]; 141 | } 142 | 143 | - (void)kl_reloadSections:(NSIndexSet *)sections { 144 | [self kl_reloadSections:sections]; 145 | [self kl_checkEmpty]; 146 | } 147 | 148 | - (void)kl_insertItemsAtIndexPaths:(NSArray *)indexPaths { 149 | [self kl_insertItemsAtIndexPaths:indexPaths]; 150 | [self kl_checkEmpty]; 151 | } 152 | 153 | - (void)kl_deleteItemsAtIndexPaths:(NSArray *)indexPaths { 154 | [self kl_deleteItemsAtIndexPaths:indexPaths]; 155 | [self kl_checkEmpty]; 156 | } 157 | 158 | - (void)kl_performBatchUpdates:(void (^)(void))updates completion:(void (^)(BOOL))completion { 159 | self.kl_isBatchUpdates = YES; 160 | __weak typeof(self) weakself = self; 161 | [self kl_performBatchUpdates:updates completion:^(BOOL finished) { 162 | if (completion) { 163 | completion(finished); 164 | } 165 | if (weakself) { 166 | weakself.kl_isBatchUpdates = NO; 167 | [weakself kl_checkEmpty]; 168 | } 169 | }]; 170 | } 171 | 172 | @end 173 | -------------------------------------------------------------------------------- /KLTableViewAndCollectionViewPlaceholder/UITableView+KLTableViewPlaceholder.h: -------------------------------------------------------------------------------- 1 | // 2 | // UITableView+KLTableViewPlaceholder.h 3 | // KLTableViewAndCollectionViewPlaceholder 4 | // 5 | // Created by 柯磊 on 2017/2/24. 6 | // Copyright © 2017年 https://github.com/klaus01 All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef UIView * __nullable (^KLTableViewPlaceholderViewBlock)(UITableView * __nonnull tableView); 12 | typedef void (^KLTableViewBackToNormalBlock)(UITableView * __nonnull tableView); 13 | 14 | @interface UITableView (KLTableViewPlaceholder) 15 | 16 | /** 17 | Call the placeholderViewBlock when the data is empty. 18 | 19 | placeholderViewBlock = nil is disabled, otherwise the null data display placeholder function is enabled. 20 | 21 | @param placeholderViewBlock Called when a placeholder needs to be displayed, returns a view. 22 | */ 23 | - (void)kl_placeholderViewBlock:(KLTableViewPlaceholderViewBlock __nullable)placeholderViewBlock; 24 | 25 | /** 26 | Called placeholderViewBlock when the data is empty, and backToNormalBlock when the data is not empty. 27 | 28 | placeholderViewBlock = nil is disabled, otherwise the null data display placeholder function is enabled. 29 | 30 | @param placeholderViewBlock Called when a placeholder needs to be displayed, returns a view. 31 | @param backToNormalBlock Called when there is data 32 | */ 33 | - (void)kl_placeholderViewBlock:(KLTableViewPlaceholderViewBlock __nullable)placeholderViewBlock 34 | backToNormalBlock:(KLTableViewBackToNormalBlock __nullable)backToNormalBlock; 35 | 36 | @end 37 | -------------------------------------------------------------------------------- /KLTableViewAndCollectionViewPlaceholder/UITableView+KLTableViewPlaceholder.m: -------------------------------------------------------------------------------- 1 | // 2 | // UITableView+KLTableViewPlaceholder.m 3 | // KLTableViewAndCollectionViewPlaceholder 4 | // 5 | // Created by 柯磊 on 2017/2/24. 6 | // Copyright © 2017年 https://github.com/klaus01 All rights reserved. 7 | // 8 | 9 | #import "UITableView+KLTableViewPlaceholder.h" 10 | #import "KLUtility.h" 11 | #import 12 | 13 | @implementation UITableView (KLTableViewPlaceholder) 14 | 15 | + (void)load { 16 | swizzleMethod(self, @selector(beginUpdates), @selector(kl_beginUpdates)); 17 | swizzleMethod(self, @selector(endUpdates), @selector(kl_endUpdates)); 18 | swizzleMethod(self, @selector(reloadData), @selector(kl_reloadData)); 19 | swizzleMethod(self, @selector(insertSections:withRowAnimation:), @selector(kl_insertSections:withRowAnimation:)); 20 | swizzleMethod(self, @selector(deleteSections:withRowAnimation:), @selector(kl_deleteSections:withRowAnimation:)); 21 | swizzleMethod(self, @selector(reloadSections:withRowAnimation:), @selector(kl_reloadSections:withRowAnimation:)); 22 | swizzleMethod(self, @selector(insertRowsAtIndexPaths:withRowAnimation:), @selector(kl_insertRowsAtIndexPaths:withRowAnimation:)); 23 | swizzleMethod(self, @selector(deleteRowsAtIndexPaths:withRowAnimation:), @selector(kl_deleteRowsAtIndexPaths:withRowAnimation:)); 24 | } 25 | 26 | #pragma mark - public methods 27 | 28 | - (void)kl_placeholderViewBlock:(KLTableViewPlaceholderViewBlock _Nullable)placeholderViewBlock { 29 | [self kl_placeholderViewBlock:placeholderViewBlock backToNormalBlock:nil]; 30 | } 31 | 32 | - (void)kl_placeholderViewBlock:(KLTableViewPlaceholderViewBlock _Nullable)placeholderViewBlock 33 | backToNormalBlock:(KLTableViewBackToNormalBlock _Nullable)backToNormalBlock { 34 | self.kl_placeholderViewBlock = placeholderViewBlock; 35 | self.kl_backToNormalBlock = backToNormalBlock; 36 | } 37 | 38 | #pragma mark - property methods 39 | 40 | - (KLTableViewPlaceholderViewBlock)kl_placeholderViewBlock { 41 | return objc_getAssociatedObject(self, @selector(kl_placeholderViewBlock)); 42 | } 43 | 44 | - (void)setKl_placeholderViewBlock:(KLTableViewPlaceholderViewBlock)kl_placeholderViewBlock { 45 | objc_setAssociatedObject(self, @selector(kl_placeholderViewBlock), kl_placeholderViewBlock, OBJC_ASSOCIATION_COPY_NONATOMIC); 46 | 47 | if (kl_placeholderViewBlock == nil) { 48 | [self kl_removePlaceholderView]; 49 | } 50 | } 51 | 52 | - (KLTableViewBackToNormalBlock)kl_backToNormalBlock { 53 | return objc_getAssociatedObject(self, @selector(kl_backToNormalBlock)); 54 | } 55 | 56 | - (void)setKl_backToNormalBlock:(KLTableViewBackToNormalBlock)kl_backToNormalBlock { 57 | objc_setAssociatedObject(self, @selector(kl_backToNormalBlock), kl_backToNormalBlock, OBJC_ASSOCIATION_COPY_NONATOMIC); 58 | } 59 | 60 | - (UIView *)kl_placeholderView { 61 | return objc_getAssociatedObject(self, @selector(kl_placeholderView)); 62 | } 63 | 64 | - (void)setKl_placeholderView:(UIView *)kl_placeholderView { 65 | objc_setAssociatedObject(self, @selector(kl_placeholderView), kl_placeholderView, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 66 | } 67 | 68 | - (NSInteger)kl_updateCount { 69 | NSNumber *number = objc_getAssociatedObject(self, @selector(kl_updateCount)); 70 | return number ? number.integerValue : 0; 71 | } 72 | 73 | - (void)setKl_updateCount:(NSInteger)kl_updateCount { 74 | objc_setAssociatedObject(self, @selector(kl_updateCount), @(kl_updateCount), OBJC_ASSOCIATION_RETAIN_NONATOMIC); 75 | } 76 | 77 | #pragma mark - private methods 78 | 79 | - (void)kl_removePlaceholderView { 80 | if (self.kl_placeholderView) { 81 | [self.kl_placeholderView removeFromSuperview]; 82 | self.kl_placeholderView = nil; 83 | self.kl_backToNormalBlock ? self.kl_backToNormalBlock(self) : nil; 84 | } 85 | } 86 | 87 | - (void)kl_checkEmpty { 88 | dispatch_async(dispatch_get_main_queue(), ^{ 89 | if (self.kl_updateCount > 0 || self.kl_placeholderViewBlock == nil) { 90 | return; 91 | } 92 | 93 | BOOL isEmpty = YES; 94 | 95 | id dataSource = self.dataSource; 96 | NSInteger sections = 1; 97 | if ([dataSource respondsToSelector:@selector(numberOfSectionsInTableView:)]) { 98 | sections = [dataSource numberOfSectionsInTableView:self]; 99 | } 100 | 101 | for (NSInteger i = 0; i < sections; i++) { 102 | NSInteger rows = [dataSource tableView:self numberOfRowsInSection:i]; 103 | if (rows) { 104 | isEmpty = NO; 105 | break; 106 | } 107 | } 108 | 109 | if (isEmpty) { 110 | UIView *placeholderView = self.kl_placeholderViewBlock ? self.kl_placeholderViewBlock(self) : nil; 111 | if (self.kl_placeholderView != placeholderView) { 112 | [self.kl_placeholderView removeFromSuperview]; 113 | self.kl_placeholderView = placeholderView; 114 | if (placeholderView) { 115 | placeholderView.translatesAutoresizingMaskIntoConstraints = NO; 116 | [self addSubview:placeholderView]; 117 | NSDictionary *views = @{@"view": placeholderView, @"superview": self}; 118 | [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[view(==superview)]|" options:0 metrics:nil views:views]]; 119 | [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[view(==superview)]|" options:0 metrics:nil views:views]]; 120 | } 121 | } 122 | } else { 123 | [self kl_removePlaceholderView]; 124 | } 125 | }); 126 | } 127 | 128 | #pragma mark - hook methods 129 | 130 | - (void)kl_beginUpdates { 131 | [self kl_beginUpdates]; 132 | self.kl_updateCount++; 133 | } 134 | 135 | - (void)kl_endUpdates { 136 | [self kl_endUpdates]; 137 | NSInteger updateCount = self.kl_updateCount - 1; 138 | if (updateCount <= 0) { 139 | self.kl_updateCount = 0; 140 | [self kl_checkEmpty]; 141 | } 142 | } 143 | 144 | - (void)kl_reloadData { 145 | [self kl_reloadData]; 146 | [self kl_checkEmpty]; 147 | } 148 | 149 | - (void)kl_insertSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation { 150 | [self kl_insertSections:sections withRowAnimation:animation]; 151 | [self kl_checkEmpty]; 152 | } 153 | 154 | - (void)kl_deleteSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation { 155 | [self kl_deleteSections:sections withRowAnimation:animation]; 156 | [self kl_checkEmpty]; 157 | } 158 | 159 | - (void)kl_reloadSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation { 160 | [self kl_reloadSections:sections withRowAnimation:animation]; 161 | [self kl_checkEmpty]; 162 | } 163 | 164 | - (void)kl_insertRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation { 165 | [self kl_insertRowsAtIndexPaths:indexPaths withRowAnimation:animation]; 166 | [self kl_checkEmpty]; 167 | } 168 | 169 | - (void)kl_deleteRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation { 170 | [self kl_deleteRowsAtIndexPaths:indexPaths withRowAnimation:animation]; 171 | [self kl_checkEmpty]; 172 | } 173 | 174 | @end 175 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 柯磊 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # KLTableViewAndCollectionViewPlaceholder 2 | ![License MIT](https://img.shields.io/badge/license-MIT-green.svg?style=flat) ![CocoaPods](https://img.shields.io/cocoapods/v/KLTableViewAndCollectionViewPlaceholder.svg?style=flat) ![CocoaPods](http://img.shields.io/cocoapods/p/KLTableViewAndCollectionViewPlaceholder.svg?style=flat) ![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat) 3 | 4 | 受 [CYLTableViewPlaceHolder](https://github.com/ChenYilong/CYLTableViewPlaceHolder) 启发,将更新数据的方法尽可能的封装(不只是`reload`),使得不用修改现有代码,及可非常容易的实现`UITableView`和`UICollectionView`的“无数据”提示,零成本。 5 | 6 | #### 相比`CYLTableViewPlaceHolder`优点: 7 | * 不用修改`reload` 8 | * 支持更多刷新数据方法。如:`insertSections:`、`deleteSections:`、`insertRowsAtIndexPaths:`等等 9 | * 使用`Auto Layout`设置`placeholderView`位置,更准确,且支持横竖屏切换 10 | * 可随时取消`placeholderView`逻辑(`[self.tableView kl_placeholderViewBlock:nil];`) 11 | * 支持`UICollectionView` 12 | 13 | ## 使用 14 | CocoaPod 15 | ``` 16 | pod 'KLTableViewAndCollectionViewPlaceholder' 17 | ``` 18 | Carthage 19 | ``` 20 | github "klaus01/KLTableViewAndCollectionViewPlaceholder" 21 | ``` 22 | Objective-C 23 | ```objective-c 24 | [self.tableView kl_placeholderViewBlock:^UIView * _Nonnull(UITableView * _Nonnull tableView) { 25 | // 这里做空数据操作,例如弹出提示 26 | 27 | // 禁止 TableView 滚动 28 | tableView.scrollEnabled = NO; 29 | // 返回无数据提示视图 30 | return placeholderView; 31 | } backToNormalBlock:^(UITableView * _Nonnull tableView) { 32 | // 这里做恢复操作 33 | 34 | // 恢复 TableView 滚动 35 | tableView.scrollEnabled = YES; 36 | }]; 37 | ``` 38 | Swift 39 | ```swift 40 | tableView.kl_placeholderViewBlock({ (tableView) -> UIView in 41 | tableView.isScrollEnabled = false 42 | return placeholderView 43 | }, backToNormalBlock: { (tableView) in 44 | tableView.isScrollEnabled = true 45 | }) 46 | ``` 47 | 48 | ## 适用场景 49 | | ![](Images/image1.jpg) | ![](Images/image2.jpg) | 50 | |-------------|-------------| 51 | 52 | # License 53 | Centipede is released under the MIT license. See LICENSE for details. 54 | 55 | --------------------------------------------------------------------------------