• iOS_动态插入或删除行


    终于效果图:

    分MVC三层设计;自己定义的Cell有两种;一种是MainCell,由ModelArr提供数据源;还有一种是插入的cell,由代码创建,而且由另外一个数组供状态数据
    数据源部分:

    //
    //  MyProjectCellModel.h
    //  动态插入删除行
    //
    //  Created by beyond on 14-10-18.
    //  Copyright (c) 2014年 com.beyond All rights reserved.
    //  列表 的cell用到的数据模型
    
    #import <Foundation/Foundation.h>
    
    @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;
    
    // 点击最右側的button,须要进行什么操作??

    弹出一个View,用于改动公布的状态???

    // 类方法,字典 转 对象 相似javaBean一次性填充 + (MyProjectCellModel *)modelWithDict:(NSDictionary *)dict; // 对象方法,设置对象的属性后,返回对象 - (MyProjectCellModel *)initWithDict:(NSDictionary *)dict; @end


    <span style="color:#330033;">//
    //  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
    </span>


    主Cell,由xib和类声明、类实现组成

    //
    //  MyProjectCell.h
    //  动态插入删除行
    //
    //  Created by beyond on 14-10-18.
    //  Copyright (c) 2014年 com.beyond All rights reserved.
    //  这个是基本的Cell,用于展示微博数据的Cell
    
    #import <UIKit/UIKit.h>
    // 定义一个block,作用是点击了 右側的button,告诉主控制器弹出一个 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右边的button被点击的时候,就会调用,弹出一个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;
        }
        // 生成一个右側button,加入到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(@"button的父类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行 右边的button",row);
        // 调用外界的控制器的block,并将cell的行号传递过去
        _wannaChangeStatusBlock(row);
    }
    
    
    
    @end
    


    附加的Cell,即当点击MainCell最右側的button时,动态加入在主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 ()<UITableViewDataSource,UITableViewDelegate> { 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最右边的button时调用 //显示 或者 隐藏 用于更改状态的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





    版权声明:本文博主原创文章,博客,未经同意不得转载。

  • 相关阅读:
    Redis_配置文件
    Redis_数据使用
    QQ登录测试用例
    JMeter性能测试入门--偏重工具的使用
    浅说《测试用例》
    axure界面功能
    性能测试相关术语
    测试用例设计和测试环境搭建
    测试需求分析
    软件测试的过程
  • 原文地址:https://www.cnblogs.com/zfyouxi/p/4850914.html
Copyright © 2020-2023  润新知