//// MyProjectCellModel.m// 動態插入刪除行//// Created by beyond on 14-10-18.// Copyright (c) 2014年 com.beyond All rights reserved.// 列表 的cell用到的資料模型#import "MyProjectCellModel.h"@implementation MyProjectCellModel// 類方法,字典 轉 對象 類似javaBean一次性填充+ (MyProjectCellModel *)modelWithDict:(NSDictionary *)dict{ // 只是調用對象的initWithDict方法,之所以用self是為了對子類進行相容 return [[self alloc]initWithDict:dict];}// 對象方法,設定對象的屬性後,返回對象- (MyProjectCellModel *)initWithDict:(NSDictionary *)dict{ // 必須先調用父類NSObject的init方法 if (self = [super init]) { // 設定對象自己的屬性 [self setValuesForKeysWithDictionary:dict]; } // 返回填充好的對象 return self;}@end
主Cell,由xib和類聲明、類實現組成
//// MyProjectCell.h// 動態插入刪除行//// Created by beyond on 14-10-18.// Copyright (c) 2014年 com.beyond All rights reserved.// 這個是主要的Cell,用於展示微博資料的Cell#import // 定義一個block,作用是點擊了 右側的按鈕,告訴主控制器彈出一個 View(包含暫停、修改、刪除),block的參數是(int row)typedef void(^cellRightBtnBlock)(int) ;@class MyProjectCellModel;@interface MyProjectCell : UITableViewCell// 表徵圖@property (nonatomic,weak)IBOutlet UIImageView *iconView;// 標題@property (nonatomic,weak)IBOutlet UILabel *titleLabel;// 微博的發布狀態【暫停、草稿、發布、到期】@property (nonatomic,weak)IBOutlet UILabel *publishStatusLabel;// 日期@property (nonatomic,weak)IBOutlet UILabel *dateLabel;// 多少人關注的數字 部分@property (nonatomic,weak)IBOutlet UILabel *numLabel;// 多少人關注的固定的文字,沒有人關注此條微博時,就不顯示@property (nonatomic,weak)IBOutlet UILabel *constLabel;// 一個成員屬性block,當cell右邊的按鈕被點擊的時候,就會調用,彈出一個view,供使用者修改微博的狀態@property (nonatomic,copy)cellRightBtnBlock wannaChangeStatusBlock;// 從xib中載入 執行個體化一個對象+ (MyProjectCell *)myProjectCell;// 返回封裝好資料之後的對象- (MyProjectCell *)cellWithCellModel:(MyProjectCellModel *)cellModel;@end
//// MyProjectCell.m// 動態插入刪除行//// Created by beyond on 14-10-18.// Copyright (c) 2014年 com.beyond All rights reserved.// 這個是主要的Cell,用於展示微博資料的Cell#import "MyProjectCell.h"#import "MyProjectCellModel.h"@implementation MyProjectCell// 從xib中載入 執行個體化一個對象+ (MyProjectCell *)myProjectCell{ // mainBundel載入xib,副檔名不用寫.xib return [[NSBundle mainBundle] loadNibNamed:@"MyProjectCell" owner:nil options:nil][0];}// 返回封裝好資料之後的對象- (MyProjectCell *)cellWithCellModel:(MyProjectCellModel *)cellModel{ // 頭像圓角 _iconView.layer.cornerRadius = 6; _iconView.layer.masksToBounds = YES; _iconView.image = [UIImage imageNamed:cellModel.icon]; _titleLabel.text = cellModel.title; _publishStatusLabel.text = cellModel.publishStatus; _dateLabel.text = cellModel.date; if ([_numLabel.text intValue]) { _constLabel.hidden = YES; } else { _numLabel.text = cellModel.num; _constLabel.hidden = NO; } // 產生一個右側按鈕,添加到cell最右方 UIButton *rightBtn = [UIButton buttonWithType:UIButtonTypeCustom]; [rightBtn setBackgroundImage:[UIImage imageNamed:@"MyProjectCellRightBtn"] forState:UIControlStateNormal]; rightBtn.frame = CGRectMake(286, 0, 34, 44); [self.contentView addSubview:rightBtn]; rightBtn.tag = 5267; [rightBtn setBackgroundImage:[UIImage imageNamed:@"MyProjectCellRightBtn.png"] forState:UIControlStateNormal]; [rightBtn addTarget:self action:@selector(rightBtnClicked:) forControlEvents:UIControlEventTouchUpInside]; return self;}// 定義宏,判斷ios7#define iOS7 [[[UIDevice currentDevice]systemVersion] floatValue] >= 7.0- (void)rightBtnClicked:(UIButton *)sender{ int row = 0; MyProjectCell *cell; UITableView *tableView; if(iOS7){ NSLog(@"按鈕的父類2:%@",[[[sender superview] superview] superview]); cell = (MyProjectCell *)[[[sender superview] superview] superview]; NSLog(@"cell的父類2:%@",[[cell superview] superview] ); tableView = ( UITableView*)[[cell superview] superview] ; }else{ // iOS6 cell = (MyProjectCell *)[sender superview]; NSLog(@"被點擊的cell:%@",cell); tableView = ( UITableView*)[cell superview]; } NSIndexPath *path = [tableView indexPathForCell:cell]; row = path.row; NSLog(@"父類1%@",[sender superview]); NSLog(@"父類2%@",[[sender superview] superview]); NSLog(@"父類3%@",[[[sender superview] superview]superview]); NSLog(@"父類4%@",[[[[sender superview] superview]superview]superview]); NSLog(@"點擊了第%d行 右邊的按鈕",row); // 調用外界的控制器的block,並將cell的行號傳遞過去 _wannaChangeStatusBlock(row);}@end
附加的Cell,即當點擊MainCell最右側的按鈕時,動態添加在主Cell下方
//// ChangeStatusCell.m// 動態插入刪除行//// Created by beyond on 14-10-18.// Copyright (c) 2014年 com.beyond All rights reserved.// 附加的Cell,當點擊MainCell最右側的按鈕時,動態添加在主Cell下方#import "ChangeStatusCell.h"@implementation ChangeStatusCell// 附加的cell,沒有xib,由代碼建立- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier{ self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]; if (self) { // 添加三個按鈕,並設定tag UIButton *b1 = [self createButtonWithFrame:CGRectMake(0 , 0, 106.5, 44) Target:self Selector:@selector(btnAction:) Image:@"StatusPause_gray.png" ImagePressed:@"StatusPause_blue.png"]; b1.tag = 1; UIButton *b2 = [self createButtonWithFrame:CGRectMake(106.5, 0, 106.5, 44) Target:self Selector:@selector(btnAction:) Image:@"StatusModify_gray.png" ImagePressed:@"StatusModify_blue.png"]; b2.tag = 2; UIButton *b3 = [self createButtonWithFrame:CGRectMake(213, 0, 106.5, 44) Target:self Selector:@selector(btnAction:) Image:@"StatusTrash_gray.png" ImagePressed:@"StatusTrash_blue.png"]; b3.tag = 3; // 添加到contentView,否則無法接收到事件 // 例如:直接添加一個按鈕到tableView裡面 是無法接收到事件的;因為tableView它不會把事件傳遞給它體系之外的任何控制項 [self.contentView addSubview:b1]; [self.contentView addSubview:b2]; [self.contentView addSubview:b3]; } return self;}- (void)btnAction:(id)sender { UIButton *btn = (UIButton *)sender; switch (btn.tag) { case 1: { NSLog(@">>>>>>>>>>B1"); } break; case 2: { NSLog(@">>>>>>>>>>B2"); } break; case 3: { NSLog(@">>>>>>>>>>B3"); } break; default: break; } }#pragma mark - 工具方法- (UIButton*) createButtonWithFrame: (CGRect) frame Target:(id)target Selector:(SEL)selector Image:(NSString *)image ImagePressed:(NSString *)imagePressed{ UIButton * button = [UIButton buttonWithType:UIButtonTypeCustom]; [button setFrame:frame]; UIImage *newImage = [UIImage imageNamed: image]; [button setBackgroundImage:newImage forState:UIControlStateNormal]; UIImage *newPressedImage = [UIImage imageNamed: imagePressed]; [button setBackgroundImage:newPressedImage forState:UIControlStateHighlighted]; [button addTarget:target action:selector forControlEvents:UIControlEventTouchUpInside]; return button;}@end
控制器,繼承自UIViewController,最核心的部分
//// MyProjectController.m// 動態插入刪除行//// Created by beyond on 14-10-18.// Copyright (c) 2014年 com.beyond All rights reserved.// 控制器,最最重要的部分#import "MyProjectController.h"#import "MyProjectCell.h"#import "ChangeStatusCell.h"#import "MyProjectCellModel.h"// 非常好用的,快速建立rect並且適配的宏#define IsIOS7 ([[[[UIDevice currentDevice] systemVersion] substringToIndex:1] intValue]>=7)#define CGRECT_NO_NAV(x,y,w,h) CGRectMake((x), (y+(IsIOS7?20:0)), (w), (h))#define CGRECT_HAVE_NAV(x,y,w,h) CGRectMake((x), (y+(IsIOS7?64:0)), (w), (h))#define IS_IOS7 [[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0@interface MyProjectController (){ UITableView *_tableView; // 資料來源 對象 數組 NSMutableArray *_modelArr; // 輔助的狀態 字典 數組;長度與上面的modelArr一樣 NSMutableArray *_statusDictArr; // 記錄已經展開的行號 int _ExpandedMainCellRow;}@end@implementation MyProjectController- (void)viewDidLoad{ [super viewDidLoad]; if (IS_IOS7) { self.edgesForExtendedLayout = UIRectEdgeNone; self.extendedLayoutIncludesOpaqueBars = NO; self.modalPresentationCapturesStatusBarAppearance = NO; } // 添加tableView,並設定代理和資料來源 UITableView *tableView = [[UITableView alloc]initWithFrame:CGRECT_NO_NAV(0, 0, 320, 460) style:UITableViewStylePlain]; tableView.dataSource = self; tableView.delegate = self; [self.view addSubview:tableView]; _tableView = tableView; _ExpandedMainCellRow = -1; // 提供資料模型的數組 _modelArr = [NSMutableArray array]; // 用於記錄 主Cell和附加Cell 以及 附加狀態的數組;二者長度相同 _statusDictArr = [NSMutableArray array]; // 載入資料來源 [self loadData];}#pragma mark - 初始化資料- (void)loadData{ NSString *filePath = [[NSBundle mainBundle]pathForResource:@"MyProjectList.plist" ofType:nil]; NSArray *tempArr = [NSArray arrayWithContentsOfFile:filePath]; // 非常重要!!!!!!!僅供狀態數組 使用 NSDictionary *statusDict = @{ @"Cell": @"MyProjectCell", @"isAttached":@(NO) }; for (NSDictionary *dict in tempArr) { MyProjectCellModel *model = [MyProjectCellModel modelWithDict:dict]; [_modelArr addObject:model]; [_statusDictArr addObject:statusDict]; }}#pragma mark - TableView代理方法// 取消預設點擊cell的選中效果- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{ [tableView deselectRowAtIndexPath:indexPath animated:YES];}// 行高- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{ return 44;}#pragma mark - TableView資料來源// 總的行數- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{ return _statusDictArr.count;}// 核心方法;分情況建立cell;或者為cell填充資料模型- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ if ([[_statusDictArr[indexPath.row] objectForKey:@"Cell"] isEqualToString:@"MyProjectCell"]) { static NSString *cellID_1 = @"MyProjectCell"; MyProjectCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID_1]; if (cell == nil) { cell = [MyProjectCell myProjectCell]; cell.selectionStyle = UITableViewCellSelectionStyleGray; __unsafe_unretained MyProjectController *blockCtroller = self; cell.wannaChangeStatusBlock = ^(int row){ NSLog(@"控制器中拿到被點擊的cell的行號: %d",row); // 調用控制器的方法,顯示 或者 隱藏 用於更改狀態的view [blockCtroller showHideChangeStatusView:row]; }; } // 傳遞資料來源 // 設定cell中獨一無二的內容 int tempRow = indexPath.row; if (_ExpandedMainCellRow != -1 && indexPath.row > _ExpandedMainCellRow) { tempRow = indexPath.row - 1; } // 如果是由於 剛才展開的行,被關閉,而導致 本方法被調用;則沒有展開的,還是按原來的位置取資料模型 if (_ExpandedMainCellRow == -1) { tempRow = indexPath.row; } MyProjectCellModel *model = [_modelArr objectAtIndex:tempRow]; cell = [cell cellWithCellModel:model]; return cell; }else if([[_statusDictArr[indexPath.row] objectForKey:@"Cell"] isEqualToString:@"ChangeStatusCell"]){ static NSString *cellID_2 = @"ChangeStatusCell"; ChangeStatusCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID_2]; if (cell == nil) { cell = [[ChangeStatusCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellID_2]; cell.selectionStyle = UITableViewCellSelectionStyleNone; } return cell; } return nil;}- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView{ // 開始滾動之前先關閉已經展開的AttachRow}#pragma mark - 點擊了cell最右邊的按鈕時調用//顯示 或者 隱藏 用於更改狀態的view- (void)showHideChangeStatusView:(int)row{ if (row == _ExpandedMainCellRow) { // 如果這次點擊的行 已經是開啟的行,則直接關閉,並返回 [self closeOneExpandedCell:_ExpandedMainCellRow]; }else{ if (_ExpandedMainCellRow == -1) { //代表從來沒有展開過,首次展開 [self expandOneRow:row]; } else { // 如果本次點擊的行還沒有展開,先關閉上次的展開的行;再展開開次的行 // 特別注意,這個row行號是包含了已經展開的AttachCell,因此,要 減去 1;此時的行號才是modelArr中的行號 if (_ExpandedMainCellRow < row) { // 展開的行,在上面;新點擊的行 在下面 row = row - 1; } [self closeOneExpandedCell:_ExpandedMainCellRow]; [self expandOneRow:row]; } }}// 關閉一個指定行的ExpandedCell- (void)closeOneExpandedCell:(int)row{ // 因為本操作執行完成後,就沒有展開的行,所以要先清零;必須先清零~~~~因為tableViewupdate方法中會調用cellForRow方法 _ExpandedMainCellRow = -1; // 如果第0行已經是附加了,則關閉附加cell NSDictionary * dic = @{@"Cell": @"MyProjectCell",@"isAttached":@(NO)}; // 主Cell中的bool還原為NO _statusDictArr[row] = dic; NSLog(@"刪除前:%d",[_statusDictArr count]); // 主Cell數組中的的下一個位置處的AttchedCell刪除 [_statusDictArr removeObjectAtIndex:row+1]; NSLog(@"刪除後:%d",[_statusDictArr count]); [_tableView beginUpdates]; NSIndexPath *path = [NSIndexPath indexPathForRow:row+1 inSection:0]; [_tableView deleteRowsAtIndexPaths:@[path] withRowAnimation:UITableViewRowAnimationMiddle]; [_tableView endUpdates]; }//記錄下 並且 展開指定的行 (順序千萬不能反)- (void)expandOneRow:(int)row{ _ExpandedMainCellRow = row; // 如果第0行沒有開啟附加的cell,則開啟附加cell NSDictionary * dic = @{@"Cell": @"MyProjectCell",@"isAttached":@(YES)}; // 修改主Cell的狀態字 isAttached為yes _statusDictArr[row] = dic; // 插入一個新的ChangeStatusCell到數組中 NSDictionary * addDic = @{@"Cell": @"ChangeStatusCell",@"isAttached":@(YES)}; [_statusDictArr insertObject:addDic atIndex:row + 1]; [_tableView beginUpdates]; NSIndexPath *path = [NSIndexPath indexPathForRow:row + 1 inSection:0]; [_tableView insertRowsAtIndexPaths:@[path] withRowAnimation:UITableViewRowAnimationMiddle]; [_tableView endUpdates]; if (row == _modelArr.count -1) { // 如果展開的是最後一行,還要讓tableView向上滾動44的高度 [self showExpandedCellForLastRow]; }}// 如果展開的是最後一行,還要讓tableView向上滾動44的高度- (void)showExpandedCellForLastRow{ NSIndexPath *path = [NSIndexPath indexPathForRow:_modelArr.count inSection:0]; [_tableView scrollToRowAtIndexPath:path atScrollPosition:UITableViewScrollPositionMiddle animated:YES];}@end