分MVC三層設計;自定義的Cell有兩種;一種是MainCell,由ModelArr提供數據源;另一種是插入的cell,由代碼創建,並且由另外一個數組供狀態數據<喎?/kf/ware/vc/" target="_blank" class="keylink">vc3Ryb25nPjxicj4KCgrK/b7d1LSyv7fWOjxicj4KCgo8aW1nIHNyYz0="/uploadfile/Collfiles/20141020/20141020084601125.png" alt="\">
//
// MyProjectCellModel.h
// 動態插入刪除行
//
// Created by beyond on 14-10-18.
// Copyright (c) 2014年 com.beyond All rights reserved.
// 列表 的cell用到的數據模型
#import
@interface MyProjectCellModel : NSObject
// 圖標
@property (nonatomic,copy) NSString *icon;
// 標題
@property (nonatomic,copy) NSString *title;
// 發布狀態
@property (nonatomic,copy) NSString *publishStatus;
// 日期
@property (nonatomic,copy) NSString *date;
// 多少人關注
@property (nonatomic,copy) NSString *num;
// 點擊最右側的按鈕,需要進行什麼操作??彈出一個View,用於修改發布的狀態???
// 類方法,字典 轉 對象 類似javaBean一次性填充
+ (MyProjectCellModel *)modelWithDict:(NSDictionary *)dict;
// 對象方法,設置對象的屬性後,返回對象
- (MyProjectCellModel *)initWithDict:(NSDictionary *)dict;
@end
//
// 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