//*****UITableView相关知识点*****// 1 #import "ViewController.h" 2 3 // step1 要实现UITableViewDataSource协议,因为tableView不存储数据 4 @interface ViewController () <UITableViewDataSource, UITableViewDelegate> 5 6 // step0 准备要显示的数据 7 @property(nonatomic,strong) NSMutableArray *dataArrM; 8 9 @property(nonatomic,strong) UITableView *myTable; 10 11 // 这个数组保存着每个分组的折叠状态 12 @property(nonatomic,strong) NSMutableArray *sectionShowArrM; 13 14 @end 15 16 @implementation ViewController 17 18 - (void)viewDidLoad { 19 [super viewDidLoad]; 20 [self myTable]; 21 22 // 设置导航条 23 [self setNavigation ]; 24 } 25 26 -(void)setNavigation{ 27 28 UIBarButtonItem *rightItem = [[UIBarButtonItem alloc]initWithTitle:@"Edit" style:UIBarButtonItemStylePlain target:self action:@selector(tableEdit:)]; 29 30 self.navigationItem.rightBarButtonItem = rightItem; 31 } 32 // 点击导航右侧编辑按钮时,让表格可编辑 33 -(void)tableEdit:(UIBarButtonItem *) btnItem{ 34 35 if (self.myTable.editing == NO ) { // 没有处于编辑状态,导航按钮文字为“Edit” 36 // 点击“编辑”文字,让表格处于编辑状态,并把按钮的文字修改为“Done" 37 self.myTable.editing = YES; 38 btnItem.title = @"Done"; 39 // 创建一个新删除按钮 40 UIBarButtonItem *deleteBtn = [[UIBarButtonItem alloc]initWithTitle:@"Delete" style:UIBarButtonItemStylePlain target:self action:@selector(DeleteAllSeleted )]; 41 42 self.navigationItem.rightBarButtonItems = @[btnItem, deleteBtn]; 43 44 }else{ 45 // 编辑状态下,点击”Done"按钮,取消表格的编辑状态,修改导航按钮文字为"Edit" 46 self.myTable.editing = NO; 47 btnItem.title = @"Edit" ; 48 self.navigationItem.rightBarButtonItems = @[btnItem]; 49 } 50 51 } 52 53 // 删除选中的所有行 54 -(void)DeleteAllSeleted{ 55 // 获取你选中的所有行 56 NSArray *selectedIndexPaths = self.myTable.indexPathsForSelectedRows; 57 // 删除数据源 58 // for (NSIndexPath *indexPath in selectedIndexPaths ) { 59 // // 0 1 2 3 4 5 6 7 8 60 // // 1 2 3 4 5 6 7 8 61 // // 1 3 4 5 6 7 8 62 // // 1 3 5 6 7 8 63 // // 0 1 2 64 // [self.dataArrM[indexPath.section] removeObjectAtIndex:indexPath.row]; 65 // } 66 // 需要注意的是,一起删除数组中多个元素的时候,需要从数组的大索引值依次向小索引值删除 67 for (long i = selectedIndexPaths.count - 1 ; i >= 0 ; i-- ) { 68 NSIndexPath *indexPath = selectedIndexPaths[i]; 69 [self.dataArrM[indexPath.section] removeObjectAtIndex:indexPath.row]; 70 } 71 // 在表格中删除单元格 72 [self.myTable deleteRowsAtIndexPaths:selectedIndexPaths withRowAnimation:UITableViewRowAnimationAutomatic]; 73 74 for (int i = 0 ; i < self.dataArrM.count; i++ ) { 75 NSMutableArray *arr = self.dataArrM[i]; 76 for (int j = 0 ; j < arr.count ; j++ ) { 77 NSLog(@"%@", arr[j]); 78 } 79 } 80 } 81 82 #pragma mark - tableView 的懒加载 83 // step2 在当前视图中添加tableView 84 -(UITableView *)myTable{ 85 if ( !_myTable) { 86 _myTable =[[ UITableView alloc]initWithFrame:self.view.bounds style:UITableViewStyleGrouped]; 87 88 // _myTable = [[UITableView alloc]initWithFrame:CGRectMake(0, 64, self.view.bounds.size.width, self.view.bounds.size.height - 64 ) style:UITableViewStyleGrouped]; 89 [self.view addSubview:_myTable ]; 90 91 // step3 指定数据源代理, tableview的数据由当前控制器提供 92 _myTable.dataSource = self; //*********千万别忘记********** 93 _myTable.delegate = self; //*********千万别忘记********** 94 95 // 设置表格的footerView 96 UIView *footerView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, _myTable.frame.size.width, 30)]; 97 footerView.backgroundColor = [UIColor redColor]; 98 _myTable.tableFooterView = footerView; 99 100 // 设置表格允许多选 101 _myTable.allowsMultipleSelection = YES; 102 _myTable.allowsSelectionDuringEditing = YES; 103 _myTable.allowsMultipleSelectionDuringEditing = YES; 104 } 105 return _myTable; 106 } 107 108 #pragma mark - 数组懒加载 109 -(NSMutableArray *)dataArrM{ 110 if ( !_dataArrM ) { 111 _dataArrM = [NSMutableArray array]; 112 for (int i = 0 ; i < 5 ; i++ ) { 113 NSMutableArray *sectionArrM = [NSMutableArray array]; 114 for (int j = 0 ; j < 15 ; j++ ) { 115 NSString *str = [NSString stringWithFormat:@"第%d组,第%d行", i ,j ]; 116 [sectionArrM addObject:str]; 117 } 118 119 [_dataArrM addObject:sectionArrM]; 120 } 121 } 122 return _dataArrM; 123 } 124 125 #pragma mark - 分组是否折叠数据的懒加载 126 -(NSMutableArray *)sectionShowArrM{ 127 if (!_sectionShowArrM ) { 128 _sectionShowArrM = [NSMutableArray array]; 129 for (int i = 0 ; i < self.dataArrM.count ; i++ ) { 130 [_sectionShowArrM addObject:@NO]; // 默认起始状态为收起 131 } 132 } 133 return _sectionShowArrM; 134 } 135 136 137 // step4 实现UITableViewDataSource协议方法 138 #pragma mark - UITableViewDataSource 139 // 返回有多少分组 140 -(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{ 141 return self.dataArrM.count; 142 } 143 // 指定每个分组有多少行,返回0行表示该分组不显示单元格 144 -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{ 145 if ([self.sectionShowArrM[section] boolValue] == YES) { 146 // 如果sectionShowArrM数组中保存当前分组的状态是YES,为展开状态,返回数组元素的个数 147 return [self.dataArrM[section] count]; 148 }else{ 149 return 0; // 收起状态 150 } 151 152 } 153 // 指定要显示的单元格 154 -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ 155 // 注册单元格类,并指定重用标识 156 [tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"cellID"]; 157 // 从tableView中找一个可重用的单元格,如果没有的话,系统会自动创建一个新的单元格 158 UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cellID" forIndexPath:indexPath]; 159 // 指定单元格要显示的内容 160 cell.textLabel .text = self.dataArrM[indexPath.section][indexPath.row]; 161 162 //返回单元格 163 return cell; 164 } 165 // 每个分组的头标题 166 - (nullable NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section{ 167 168 return [NSString stringWithFormat:@"第%ld组的头", section]; 169 } 170 // 每个分组尾部的标题 171 - (nullable NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section{ 172 return [NSString stringWithFormat:@"第%ld组的尾部", section]; 173 174 } 175 176 // 指示是否允许编辑,具体的删除和插入操作由其他协议方法实现 177 - (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath{ 178 return YES; 179 } 180 181 // 指示表格中的行是否可以移动 182 - (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath{ 183 return YES; 184 } 185 186 // 返回组索引的标题 187 - (nullable NSArray<NSString *> *)sectionIndexTitlesForTableView:(UITableView *)tableView{ 188 NSMutableArray *sectionTitles = [NSMutableArray array]; 189 for (int i = 0 ; i < self.dataArrM.count ; i++ ) { 190 [sectionTitles addObject: [NSString stringWithFormat:@"%d组", i ]]; 191 } 192 return sectionTitles; 193 } 194 195 //- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index{ 196 // if (index == 2 ) { 197 // return 0; 198 // }else { 199 // return index; 200 // } 201 //} 202 203 // 提交单元格的编辑 204 - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath{ 205 if (editingStyle == UITableViewCellEditingStyleDelete) { 206 // 在数据源数组中删除相应的数据 207 [self.dataArrM[indexPath.section] removeObjectAtIndex:indexPath.row]; 208 // 刷新表格 209 [self.myTable deleteRowsAtIndexPaths:@[indexPath ] withRowAnimation:UITableViewRowAnimationAutomatic]; 210 }else if ( editingStyle == UITableViewCellEditingStyleInsert){ 211 // 在数据源数组中插入数据 212 [self. dataArrM[indexPath.section] insertObject:@"新插入的行" atIndex:indexPath.row ]; 213 // 刷新单元格 214 [self.myTable insertRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic]; 215 } 216 217 } 218 // 在该方法中一般实现数据源的交换 219 - (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath{ 220 // 取出要移动行的内容 221 NSString *row = self.dataArrM[sourceIndexPath.section][sourceIndexPath.row]; 222 // 在数据源中删除要移动的行 223 [self.dataArrM[sourceIndexPath.section] removeObjectAtIndex:sourceIndexPath.row]; 224 // 在目的地插入要移动的行数据 225 [self.dataArrM[destinationIndexPath.section] insertObject:row atIndex:destinationIndexPath.row]; 226 } 227 228 #pragma mark - UITableViewDelegate 229 // 将要显示每个分组的footer的时候调用 230 - (void)tableView:(UITableView *)tableView willDisplayFooterView:(UIView *)view forSection:(NSInteger)section{ 231 232 // if (section < self.dataArrM.count - 1 ) { 233 // return; 234 // } 235 // 236 // //获取更多数据 237 // NSMutableArray *addRowsArrM = [NSMutableArray array]; 238 // for (int i = 0 ; i < 10 ; i++ ) { 239 // NSString *addRow = [NSString stringWithFormat:@"新添加的第%d行", i ]; 240 // [addRowsArrM addObject:addRow]; 241 // } 242 // [self.dataArrM addObject:addRowsArrM]; 243 // //刷新表格 244 // NSIndexSet *indexSet = [[NSIndexSet alloc]initWithIndex:section]; 245 // [self.myTable reloadSections:indexSet withRowAnimation:UITableViewRowAnimationAutomatic]; 246 } 247 248 // 返回第一行的高度 249 - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{ 250 return 80; 251 } 252 // 返回每个分组头部的高度 253 - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section{ 254 return 40; 255 } 256 // 返回每个分组尾部的高度 257 - (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section{ 258 return 20; 259 } 260 261 // 返回每个分组头部视图,如果实现了这个协议,tableView:titileForHeaderInSection协议方法不再起作用 262 - (nullable UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section{ 263 UIButton *headerBtn = [[UIButton alloc]initWithFrame:CGRectMake(0, 0, tableView.frame.size.width, 40)]; 264 [headerBtn setTitle:[NSString stringWithFormat: @"这是第%ld组的头部", section] forState:UIControlStateNormal] ; 265 headerBtn.titleLabel.textAlignment = NSTextAlignmentCenter; 266 headerBtn.backgroundColor = [UIColor orangeColor]; 267 268 // [headerBtn setImage:[UIImage imageNamed:@"icon_back_highlighted"] forState:UIControlStateNormal]; 269 UIImageView *btnImg = [[UIImageView alloc]initWithFrame:CGRectMake(0, 0, 30, headerBtn.frame.size.height)]; 270 btnImg.contentMode = UIViewContentModeScaleAspectFit; 271 btnImg.image = [UIImage imageNamed:@"icon_back_highlighted"]; 272 [headerBtn addSubview:btnImg]; 273 274 // 根据当前分组是否折叠来确定图标的旋转 275 BOOL isShow = [self.sectionShowArrM[section] boolValue]; 276 if (isShow == YES) { 277 btnImg.transform = CGAffineTransformRotate(btnImg.transform, -M_PI_2); 278 }else{ 279 btnImg.transform = CGAffineTransformIdentity; 280 } 281 282 headerBtn.tag = 10 + section; 283 // 添加事件 284 [headerBtn addTarget:self action:@selector(showSection: ) forControlEvents:UIControlEventTouchUpInside]; 285 286 return headerBtn; 287 } 288 -(void)showSection:(UIButton *)button{ 289 long sectionIndex = button.tag - 10; 290 // 先获取当前分组的折叠状态 291 BOOL isShow = [self.sectionShowArrM[sectionIndex] boolValue]; 292 if (isShow == NO ) { // 如果收起状态,点一下后,修改为展开状态 293 self.sectionShowArrM[sectionIndex] = @YES; 294 }else { // 展开状态 295 self.sectionShowArrM[sectionIndex] = @NO; 296 } 297 // 刷新当前分组 298 NSIndexSet *indexSet = [[NSIndexSet alloc] initWithIndex:sectionIndex]; 299 [self.myTable reloadSections:indexSet withRowAnimation:UITableViewRowAnimationAutomatic]; 300 301 // 让分组头部左侧的图标旋转,这块代码放在这儿不合适,表格刷新后,图标会复位 302 // 获取当前按钮中的箭头 303 // UIImageView *btnImg; 304 // for (UIView *subView in button.subviews) { 305 // if ([subView isKindOfClass:[UIImageView class]]) { 306 // btnImg = (UIImageView *)subView; 307 // break; 308 // } 309 // } 310 // 311 // btnImg.transform = CGAffineTransformRotate(btnImg.transform , -M_PI_2); 312 } 313 314 // 返回每个分组尾部视图 315 // - (nullable UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section{ 316 // 317 // } 318 319 // 返回单元格右侧的辅助按钮的类型 320 - (UITableViewCellAccessoryType)tableView:(UITableView *)tableView accessoryTypeForRowWithIndexPath:(NSIndexPath *)indexPath { 321 return UITableViewCellAccessoryDisclosureIndicator; 322 } 323 // 当单元格右侧的辅助视图是一个按钮,当点击该按钮时,执行该协议方法 324 - (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath{ 325 NSLog(@"detailButton be tapped"); 326 } 327 328 // 指示是否允许高亮显示选中的行 329 - (BOOL)tableView:(UITableView *)tableView shouldHighlightRowAtIndexPath:(NSIndexPath *)indexPath{ 330 return YES; 331 } 332 333 // 选中某行时执行 334 - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{ 335 NSLog(@"selected: %ld, row:%ld", indexPath.section, indexPath.row); 336 } 337 // 取消选中时执行,这个方法常在表格允许多选时调用执行 338 - (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath{ 339 NSLog(@"Deselected: %ld, row:%ld", indexPath.section, indexPath.row); 340 } 341 342 // 返回编辑时,是删除单元格还是插入单元格,要真正的实现删除或者插入操作,必须实现tableView:commitEditingStyle:forRowAtIndexPath:这个协议方法 343 - (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath{ 344 return UITableViewCellEditingStyleInsert; 345 } 346 347 // 单元格的缩进 348 - (NSInteger)tableView:(UITableView *)tableView indentationLevelForRowAtIndexPath:(NSIndexPath *)indexPath{ 349 if(indexPath.row % 2 == 0){ 350 return 10; 351 }else{ 352 return 0; 353 } 354 } 355 356 @end
时间: 2024-10-24 14:15:35