当前位置: 澳门新濠3559 > 编程 > 正文

cell的创建就是和我们平常的一样,先来分析下

时间:2019-09-14 13:29来源:编程
先看效果 今天一个同学让我帮他做cell编辑功能,效果大致如下: 效果.gif 点击顶部编辑按钮,所有cell变为可编辑状态,按钮文字变为“完成”,cell编辑后点击按钮,所有cell进入不可

先看效果

今天一个同学让我帮他做cell编辑功能,效果大致如下:

澳门新濠3559 1效果.gif

点击顶部编辑按钮,所有cell变为可编辑状态,按钮文字变为“完成”,cell编辑后点击按钮,所有cell进入不可编辑状态。

澳门新濠3559 2计算所选中物品的总价格

缺点,要是想把编辑的内容,传递到控制器的话,好像没办法做到,因为他依赖于tableView的刷新,当你点击了右上角“完成“的按钮,他会先用self.dataArr 进行刷新,把值通过model赋值给cell, 这样的话,处在编辑状态的时候的texfield的改变,又被原来的数据源覆盖了,所以没办法,把textField 改变的值,传递回去

2.加,减,选中按钮, 功能实现

关键点说明

model是数据的载体,cell展示的内容也有model决定,所以model里不仅有3个文本的内容,还有一个标识是否是可编辑状态的属性。

#import <Foundation/Foundation.h>@interface EditableCellModel : NSObject@property (nonatomic,copy) NSString *name;@property (nonatomic,copy) NSString *range;@property (nonatomic,copy) NSString *value;/** 是否是编辑状态 */@property (nonatomic,assign) BOOL isEditState;@end

用代理,将这个cell和textField一起传过去(传cell是为了获取cell所在的indexPath,传textField是为了获取textField的文本,改变数据源这两个缺一不可):

@class EditableCell;@protocol EditableCellDelegate <NSObject>- editableCell:(EditableCell *)editableCell valueTextFieldTextDidChange:(UITextField *)sender;@end

在上述代理方法里进行处理:

#pragma mark - cell的textField文本改变时回调- editableCell:(EditableCell *)editableCell valueTextFieldTextDidChange:(UITextField *)sender{ // 获取cell所在行数 NSInteger row = [self.tableView indexPathForCell:editableCell].row; // 获取cell对应的model EditableCellModel *cellModel = self.dataArray[row]; // 修改model cellModel.value = sender.text;}

/** 编辑按钮点击 */- editButtonClicked:(UIButton *)sender{ // 改变数据源 for (EditableCellModel *cellModel in self.dataArray) { // 是否进入编辑状态取反 cellModel.isEditState = !cellModel.isEditState; } // 刷新tableView [self.tableView reloadData]; // 更新按钮文字 NSString *editButtonTitle = [sender.titleLabel.text isEqualToString:@"编辑"] ? @"完成" : @"编辑"; [sender setTitle:editButtonTitle forState:UIControlStateNormal];}

澳门新濠3559 3编辑商品信息

按钮 : 控制cell中的控件,可否编辑
方法一 : 使用通知--->可控制状态,可传值
点击了按钮,就发送通知,在cell中接收通知,执行控制可否编辑的方法,当点了”完成“, 再使用block/代理把值传递到控制器

先看效果图

澳门新濠3559 4沉迷王者荣耀,简书许久没更新,罪过。。。

购物车逻辑及实现总结

逻辑整理:当我们把有购买意向的物品加到购物车后,我们在购物车中调用接口获取购物车中的物品信息。数据源格式大概是(感觉不怎么对,但是能理解就行)

[

{@“店铺信息”:[@{物品信息},@{物品信息},@{物品信息}]}, -------》组一

{@"店铺信息":[@{物品信息}]}, -------》组二

{@”店铺信息“:[@{物品信息},@{物品信息}]} -------》组三

]

把数据源用model装起来,把数据填充到tableview中去。

1.单个商品的选择、单个店铺内所有商品的选择、结账栏下的全选

如果做得很简单的话,可以直接用系统的单选和全选方法。

最重要的两句 !!!!

TableDemo.editing=YES; 编辑状态

TableDemo.allowsMultipleSelectionDuringEditing=YES; 编辑的时候多选

cell.tintColor= [UIColorredColor]; 选中后的颜色

选中和取消选中

-tableView:(UITableView*)tableView didSelectRowAtIndexPath:(NSIndexPath*)indexPath 选中

-tableView:(UITableView*)tableView didDeselectRowAtIndexPath:(NSIndexPath*)indexPath 取消选中

方法二 : model中添加一个bool变量 --->可控制状态,不可传值
要想点了按钮,就让cell知道按钮的话,那就得在点击方法中,刷新tableView才能让走给cell赋值的方法,才能走cell中的setModel 方法, 进而控制cell中控件 是否可编辑

5.选中收藏,把数据存到本地数据库
这里强调一下,购物车数据还是应该存到后台,因为要考虑切换设备的情况,这里存到本地仅仅是为了练习数据库存图片

思路

cell展示的内容来自数据源,所以,对cell展示内容进行修改,实际上就是对数据源的修改

澳门新濠3559 5还是原来的强势

但是 ; 要是仅仅想要控制控件 是否可编辑/ cell上控件的隐藏/cell的按钮的选中未选中,model中整个bool值还是能实现的

demo下载地址:
https://git.oschina.net/wwwzz/ShoppingCartDemo

小demo一份:

demo

澳门新濠3559 6效果图

先来分析下,要实现的功能:
1.页面内容展现
这里因为找不到合适数据,文字内容是从网易新闻获取的,图片是本地的,在加一些按钮.之所以获取网易的文字展现是为了练习解决下滑动内容混乱的问题

总结及引申

编辑cell,主要就两步:

  • 第一步:改变数据源
  • 第二步:reloadData

有些cell的编辑虽然看起复杂,但原理也是这样的,比如我们公司的商城APP的购物车页面:

澳门新濠3559 7购物车页面

数量加减、选不选中、删除商品什么的,其实都是对数据源的修改。

注释:这里的删除 及 修改 都是要对数据源进行修改在刷新的

第四个功能:

主要需要注意价格计算,有数量的不需要再加需要记录下来,没有数量的加1
我的解决办法是 分成两部分,一部分加不为0的,一部分加为0的,最后加到一起乘以价格得出总价格

// 全选方法
- (void)allSelectBtnAction:(UIButton *)btn{
    //  之前有数值的
    NSInteger a = 0;
    //   之前没有数值的
    NSInteger b = 0;

    for (int i = 0 ; i < self.MyArray.count ; i  ){

        LXModel *model =  self.MyArray[i];

        LXModel *tempModel = [[LXModel alloc]init];

        tempModel.alias = model.alias;

        if (btn.selected == YES){

            tempModel.isBtnSelect = YES;

            NSInteger  tempNumber = [model.number integerValue];

            if (tempNumber == 0){
                b   ;
               tempModel.number = [NSString stringWithFormat:@"1"];
            } else  {
                tempModel.number = model.number;

                a = a   tempNumber;
            }

            tempNumber = b   a;

            _zongHeLabel.text = [NSString stringWithFormat:@"%ld",(b   a) * tempModel.DanGeJiaGe];

        } else {
          // 取消 全选
            tempModel.isBtnSelect = NO;

            NSInteger  tempNumber = 0;

            tempModel.number = [NSString stringWithFormat:@"%ld",tempNumber];
            _zongHeLabel.text = @"0";
        }

        //替换
        [self.MyArray replaceObjectAtIndex:i withObject:tempModel];
    }

       [self.mainTableView reloadData];

    // 点击  切换  button   状态
    if (btn.selected == YES){
        btn.selected = NO;

    } else {
        btn.selected = YES;
    }

}

</br>

澳门新濠3559 8头视图的代理方法

第六部分

可以确定cell 高度时 这么写可以 不需要代理 提高执行效率
_mainTableView.rowHeight = 230;

</br>
监听 tf 变化 正则表达式

- (void)textFieldDidChange:(UITextField *)tf{

    if ([self checkForNumber:tf.text] ){

    } else {
        // 防止崩溃
        if (tf.text.length > 0){
            //  不匹配删掉  字符串最后一位
            tf.text = [tf.text substringToIndex:tf.text.length - 1];
        }
    }
}


//   只能输入数字  开头不能为0
- (BOOL)checkForNumber:(NSString *)number{
    //转意符  d  \d
    NSString *regEx = @"^[1-9]\d*$";
    return [self baseCheckForRegEx:regEx data:number];
}


// 检测 正则表达式
- (BOOL)baseCheckForRegEx:(NSString *)regEx data:(NSString *)data{

    //谓词查询
    NSPredicate *card = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", regEx];
    // 判断是否匹配
    if (([card evaluateWithObject:data])) {
        return YES;
    }
    return NO;
}

收藏按钮点击事件,根据是否选中判断加入数据库

- (void)reckonButtonAction{

    // 打开数据库
    [DataBase ShareDataBase];

    ShouCangVController *sc = [[ShouCangVController alloc]init];

    for (int i = 0 ; i < self.MyArray.count ; i  ){
        LXModel *model = self.MyArray[i];

        if (model.isBtnSelect == YES){

        [[DataBase ShareDataBase]addDataBase:model];

        }
    }
    [self.navigationController pushViewController:sc animated:YES];
}

</br>
其实都不难,一个功能一个功能的实现就好,有什么问题请留言

澳门新濠3559 9

4.全选按钮实现,没有选中的加1选中的不变,再次点击全部不选

澳门新濠3559 10

3.总价格展示

//中划线

NSDictionary *attribtDic = @{NSStrikethroughStyleAttributeName: [NSNumber numberWithInteger:NSUnderlineStyleSingle]};

NSMutableAttributedString *attribtStr = [[NSMutableAttributedString alloc]initWithString:info[@"GoodsOldPrice"] attributes:attribtDic];

// 赋值

_Goods_OldPrice.attributedText = attribtStr;

第五部分

主要是使用数据库用到了FMDB,先创建一个单例

.h

Paste_Image.png

.m
创建单例

  (instancetype)ShareDataBase{
    static dispatch_once_t  onceToKen;
    dispatch_once(&onceToKen,^{
        if (ShareDB == nil) {
            ShareDB = [[DataBase alloc]init];
            [ShareDB initDataBase];
        }
    });
    return ShareDB;
}


// 重写 allocWithZone   保证单例 唯一性
  (instancetype)allocWithZone:(struct _NSZone *)zone{
    if(ShareDB == nil){
        ShareDB = [super allocWithZone:zone];
    }
    return ShareDB;
}

- (id)copyWithZone:(NSZone *)zone{

    return self;
}

创建数据库 图片转成二进制 blob 类型 取出时为 data 类型

- (void)initDataBase{
    // 获得Documents目录路径

    NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];

    // 文件路径   数据库 名
    NSString *filePath = [documentsPath stringByAppendingPathComponent:@"GWCmodel.sqlite"];


    // 实例化FMDataBase对象
    // 得到数据库
    self.db = [FMDatabase databaseWithPath:filePath];

    //打开数据库
    if ([_db open]){


        // 创建数据库  id 主键 自增   网易新闻字符串  商品选中数量  价格   图片
        BOOL result = [self.db executeUpdate:@"CREATE TABLE IF NOT EXISTS t_shopping (id integer PRIMARY KEY AUTOINCREMENT,WYString text NOT NULL,Numer text NO NULL , Price integer NOT NULL , Picture blob NO NULL);"];
        if(result){
            NSLog(@"创表成功");
            //一定要使用缓存,减少时间复杂度
            [_db shouldCacheStatements];

        }else{
            NSLog(@"创表失败");
        } 
    }
}

插入数据
谓词查询 http://www.jianshu.com/p/f63107b3c177

- (void)addDataBase:(LXModel *)model{
    // 打开数据库
    [_db open];

    //  根据  字符串 判断数据库中是否存在
    FMResultSet *resultSet = [self.db executeQuery:@"SELECT * FROM t_shopping where WYString = ?",model.alias];

    //2 遍历结果
    if([resultSet next]){

    // SET 更新内容   WHERE  判断条件   如果model.number 即数量不一样  就更新
       [_db executeUpdate:@"UPDATE t_shopping SET Numer = ?  WHERE WYString = ? ",model.number,model.alias];

    }else {
        // 不存在  插入新数据

     BOOL bResult =   [_db executeUpdateWithFormat:@"INSERT INTO t_shopping (WYString,Numer,Price,Picture) VALUES (%@ ,%@ ,%ld,%@);",model.alias,model.number,model.DanGeJiaGe,model.picture];

        if(bResult) {
            NSLog(@"数据插入成功!");
        }
        else {
            NSLog(@"数据插入失败!");
        }
    }
}

删除数据

- (void)deleteDataBase:(LXModel *)model{

    [_db open];
    // 根据 网易字符串  判断
    [_db executeUpdate:@"DELETE FROM t_shopping WHERE WYString = ?",model.alias];
}

获取所有数据

- (NSMutableArray *)getAllData{
    [_db open];

    NSMutableArray *dataArray = [[NSMutableArray alloc] init];

    FMResultSet *res = [_db executeQuery:@"SELECT * FROM t_shopping"];

    while ([res next]) {
        LXModel *model = [[LXModel alloc] init];

        model.alias = [res stringForColumn:@"WYString"];

        model.DanGeJiaGe = [[res stringForColumn:@"Price"]integerValue];

        model.number = [res stringForColumn:@"Numer"] ;

        model.picture = [res dataForColumn:@"Picture"];


        [dataArray addObject:model];

    }
    return dataArray;
}

</br>

demo中的数据源我没有放到model中去处理,其实原理都一样,我把判断各类按钮的判断字段加到数据源中去了,如果用model模型的话,自己加上相对应的字段,并设置初始值。当进行model数据源的修改时,直接进行修改。

第一个比较简单就不讲解了,具体看demo吧

定制段头的View 把section的全选按钮、点击商品、编辑的三个按钮全部装起来,里面点击的方法用代理的方法。

-tableView:(UITableView*)tableView viewForHeaderInSection:(NSInteger)section;

这是加载自定义段头的代理方法。

可以照着model看,思路清楚一点

这是一种model处理方式,还有一种就是用JsonModel来处理,一层层的写下来,原理一样。

第二个功能:

先在 cell 设置代理

Paste_Image.png

.m 传递 tag 用以区分是哪个cell 上的控件

Paste_Image.png

Paste_Image.png

看代理实现前先写model

Paste_Image.png

.m 图片是本地的所以给了固定值,价格是为了好算

Paste_Image.png

</br>
控制器的宏定义 属性 和代理

Paste_Image.png

</br>

数据源中设置代理,并把indexPath.row 当做tag值传递,区分cell,根据model中的BOOL值判断选择状态, 使用KVC修改占位符颜色

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{

    LXTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"111"];

    if(cell == nil){
        cell = [[LXTableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"111"];
    }
    cell.delegate = self;

    [cell.numberTF addTarget:self action:@selector(textFieldDidChange:) forControlEvents:(UIControlEventEditingChanged)];

    // cell 上 传递  tag
    cell.selectBtn.tag = indexPath.row;

    cell.rightButton.tag = indexPath.row;

    cell.leftButton.tag = indexPath.row;

    LXModel *model = self.MyArray[indexPath.row];

// 获取 网易新闻字符串
    [cell setCellModel:model];

    cell.selectBtn.selected = model.isBtnSelect;

    cell.PictureImage.image = [UIImage imageWithData:model.picture];

    if (cell.selectBtn.selected == YES){

        [cell.selectBtn setImage:[UIImage imageNamed:@"选中"] forState:(UIControlStateNormal)];
    }else if(cell.selectBtn.selected == NO){
        [cell.selectBtn setImage:[UIImage imageNamed:@"未选中"] forState:(UIControlStateNormal)];
    }

    if (model.number == nil || model.number <= 0) {
        cell.numberTF.text = @"";
        cell.numberTF.placeholder = @"0";
    }else if(model.number != nil){
        cell.numberTF.text = model.number;
    }

// 价格
    cell.priceLabel.text = [NSString stringWithFormat:@"%ld",model.DanGeJiaGe];

    cell.numberTF.delegate = self;

    // 使用KVC 修改占位符  颜色
    [cell.numberTF setValue:[UIColor whiteColor] forKeyPath:@"_placeholderLabel.textColor"];
    return  cell;
}

</br>
</br>
cell 选中代理方法
self.MyArray 里面装的是请求下来的网易新闻字符串
两个model是用来互相替换的,这样可以解决滑动时数据不对称

//  选中  代理
- (void)selectAtIndex:(NSInteger)row{

    LXModel *model =  self.MyArray[row];

    LXModel *tempModel = [[LXModel alloc]init];

    tempModel.alias = model.alias;

    tempModel.picture = model.picture;

    if (model.isBtnSelect == YES){

        NSInteger  tempNumber = [tempModel.number integerValue];

//  取消选中状态  直接清0
        if (tempNumber == 0){
            tempModel.number = @"";
        }

        tempModel.isBtnSelect = NO;

    } else if (model.isBtnSelect == NO){

        tempModel.isBtnSelect = YES;

        NSInteger  tempNumber = [tempModel.number integerValue];

//  选中加一
        tempNumber  ;

        tempModel.number = [NSString stringWithFormat:@"%ld",tempNumber];
    }

    //替换
    [self.MyArray replaceObjectAtIndex:row withObject:tempModel];

// 计算总和
    _zongHeLabel.text = [self ZongHeJiSuan:self.MyArray];

    [self.mainTableView reloadData];
}

</br>
加减 一样 减号注意别小于0

//   添加的  btn  代理
- (void)addButtonDelegate:(NSInteger)row{


    LXModel *model =  self.MyArray[row];

    LXModel *tempModel = [[LXModel alloc]init];

    tempModel.alias = model.alias;

    tempModel.number = model.number;


    //选中按钮  选中状态
    tempModel.isBtnSelect = YES;


    NSInteger  tempNumber = [tempModel.number integerValue];
    tempNumber  ;
    tempModel.number = [NSString stringWithFormat:@"%ld",tempNumber];

    //   替换
    [self.MyArray replaceObjectAtIndex:row withObject:tempModel];

  //计算总价格
    _zongHeLabel.text = [self ZongHeJiSuan:self.MyArray];


    [self.mainTableView reloadData];
}



// 减号  代理  方法
- (void)jianButtonDelegate:(NSInteger)row{

    LXModel *model =  self.MyArray[row];

    LXModel *tempModel = [[LXModel alloc]init];

    tempModel.alias = model.alias;

    tempModel.number = model.number;


    NSInteger reduce  = [tempModel.number integerValue];
    reduce--;
    tempModel.isBtnSelect = YES;

        if (reduce <= 0 ) {
            reduce = 0 ;
          tempModel.isBtnSelect = NO;
        }

    tempModel.number = [NSString stringWithFormat:@"%ld",reduce];
    //替换
    [self.MyArray replaceObjectAtIndex:row withObject:tempModel];
    // 计算总价格
    _zongHeLabel.text = [self ZongHeJiSuan:self.MyArray];

    [self.mainTableView reloadData];
}

</br>

建议使用Masonry进行cell适配

第三个功能:

把cell 上的数字加到一个数组中,然后用valueForKeyPath 计算数组和

这里价格是写死的,不一样的时候 先让数量 乘以 价格 得出 单独商品价格,再把单独商品价格 加到数组中,计算总和

#pragma mark ===== 总和计算
- (NSString *)ZongHeJiSuan:(NSMutableArray *)arrary{

    NSMutableArray *arr = [NSMutableArray array];
    for (int i = 0; i < arrary.count; i  ) {
        LXModel *model = arrary[i];

        [arr addObject:[NSString stringWithFormat:@"%ld",model.DanGeJiaGe * [model.number integerValue]]];

    }
    // 计算 数组之和
    NSNumber *sum = [arr valueForKeyPath:@"@sum.self"];

    return  [NSString stringWithFormat:@"%@",sum];
}

</br>

澳门新濠3559,以店铺为section:商店下的商品为row和店铺名称组成一个 section

6.其他功能

创建两种cell,一个是正常的物品显示cell,另一个cell是编辑后的cell。

Paste_Image.png

正常的cell:只说下label中划线的实现

商品数量用的是第三方PPNumberButton,点击时改变model中该商品的实际数量;点击商品种类进行重新选择(方法未实现,原理一样);点击删除进行cell的删除及数据源的删除。

cell的创建就是和我们平常的一样,把要展示的样式代码编写或者xib都可以。再把数据源填充到我们所创建好的cell中和段头上。

编辑后的cell:

主要由三部分组成。商品数量 商品种类 删除

demo纯代码编写的,只隔离了部分模块,因为我也是拿来练练手,所以如果有需要,后续我会把购物车模块化。如果内容有不妥和臃肿的地方,大家可以提出来,我及时学习并修改。如果大家有意见的可以@我1804094055qq.com。如果觉得可以请大家不吝star。

整个界面就是TableView 底部结账栏View组成

如果不用系统的话,则利用model来进行单选和全选的操作,选中和取消选中对model中的判断字段进行修改,刷新当前cell或者section。

//一个section刷新

NSIndexSet *indexSet=[[NSIndexSet alloc]initWithIndex:section];

[tableview reloadSections:indexSet

withRowAnimation:UITableViewRowAnimationAutomatic];

//一个cell刷新

NSIndexPath *indexPath=[NSIndexPath indexPathForRow:row inSection:section];

[tableView reloadRowsAtIndexPaths:[NSArray arrayWithObjects:indexPath,nil] withRowAnimation:UITableViewRowAnimationNone];

这里需要注意的是,每次单选和全选的时候,需要对底部结账栏进行数据的刷新,并且单选完整组后,需要对section上的按钮进行选中状态变化,当选中section时,同样需要对section下的row进行选中状态,如果全部商品选中,还得需要改变结账栏的状态。(这里其实不难,无非就是对model中各类字段进行改变,再刷新,同时判断选中数量的多少来进行按钮的状态变换)

2.删除单个商品

删除的话就相对容易了,直接对数据源删除对应的row,当只剩一个后,删除该数据后,记得删除该组section不然报错。删除后及时刷新底部结账栏金额显示。

3.编辑section

点击编辑按钮,修改model,展示编辑cell。编辑按钮上放了数量计数器、商品的信息、删除。

计数器:用到的是好友的一个库PPNumberButton 喜欢的大家可以去玩玩。点击后用代理方法把数量的变化跟新model。

商品信息:点击对商品的信息进行重新选择,同样修改数据源。

删除:代理出来进行model的修改。

因为这里用的是假数据,所以进行的都是对数据源的修改,正常情况下,原理都一样,可以在次基础上,如果接口成功,就对本地数据进行修改,最后提交的信息会和后台匹配一次的,如果有问题,可以自己修改一下。

有小伙伴提出demo中没有下拉刷新,其实下拉刷新不影响该demo。不过加上效果更好。

谢谢“爱在巴黎梦醒时”该小伙伴。

demo的bug注释:

因为demo中判断section的全选和编辑的按钮都是放在每个section的第一个row中的,所以删除section的第一个row后,会有全选和编辑的固定bug出来。特此声明,该bug不影响主体逻辑,如次bug影响小伙伴对逻辑思路的学习,那我后面再重新组织数据源。

1.在刷新底部结账栏的总金额时,同时跟新全选状态。(及底部结账栏的UI都要统一刷新一次)

澳门新濠3559 11model

创建好一个View添加在TableView的下方。View上写上全选及总金额等UI。每次我们选定的物品的增减都要调用该View赋值的方法,刷新金额等字段显示。

使用masonry进行适配会省去我们要考虑的label换行及各类UI适配问题,切记在写约束时不要写高度。

编辑:编程 本文来源:cell的创建就是和我们平常的一样,先来分析下

关键词: 澳门新濠3559