zoukankan      html  css  js  c++  java
  • 仿购物车的实现

    基本实现了全选,删除等按钮点击功能


    一.  首先,设置好基本的购物车界面,实现tableView的相关的 数据源 和 代理 方法
    在QHLShoppingCarController.m中写一个类扩展,定义下列属性:

    /** 模型数组 */
    @property (nonatomic, strong) NSMutableArray *shoppingCar;
    /** 底部结算view */
    @property (nonatomic, weak) QHLSettleMentView *settleMentView;
    /** 底部隐藏的按钮view */
    @property (nonatomic, weak) QHLHiddenView *hiddenView;
    /** 购物车界面状态  */
    @property (nonatomic, assign) QHLViewState state;
    /** 用来保存按钮选中时候的按钮字典数组 */
    @property (nonatomic, strong) NSMutableArray *btnsArray;
    /**  存储编辑状态下选中时对应的沙盒中的按钮字典数组 */
    @property (nonatomic, strong) NSMutableArray *documentArray;
    /** 存储在沙盒中的数据数组 */
    @property (nonatomic, strong) NSMutableArray *tempArray;
    /** 选中商品数量 */
    @property (nonatomic, assign) NSInteger count;
    /** 选中商品金额 */
    @property (nonatomic, assign) NSInteger money;

    在懒加载中,tempArray是用来保存存储在应用沙盒中的字典数组

    - (NSMutableArray *)tempArray {
        if (!_tempArray) {
            
            _tempArray = [NSKeyedUnarchiver unarchiveObjectWithFile:[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0] stringByAppendingPathComponent:@"goods.archive"]];
            
        }
        return _tempArray;
    }

    抽取的方法 : 根据不同的self.state状态来设置不同的底部view的全选按钮的选中状态

    #pragma mark - 根据不同的选中状态来设置底部view的全选按钮的选中状态
    - (void)setButtonSelectState:(BOOL)selected {
        if (self.state == QHLViewStateNormal) { //根据状态来设置不同的全选按钮的选中
            self.settleMentView.btnSelected = selected;
        } else {
            self.hiddenView.allSelBtnSelected = selected;
        }
    }

    抽取的方法 : 根据不同的选中状态来对传入的数组做不同的操作

    #pragma mark - 根据不同的选中状态来对数组进行操作
    - (void)handleObjectInArrays:(id)btnsObject documentsObject:(id)documentsObject selectedState:(BOOL)selected{
        
        if (selected) { //选中时
            [self.documentArray addObject:documentsObject];
            [self.btnsArray addObject:btnsObject];
        } else { //取消选中时
            [self.documentArray removeObject:documentsObject];
            [self.btnsArray removeObject:btnsObject];
        }
    }

    二.  在自定义的cell中,cell中的选中button的点击事件通过代理放在控制器中去处理:

    在QHLShoppingCarController.m中实现cell的代理方法

    - (void)cell:(QHLTableViewCell *)cell selBtnDidClickToChangeAllSelBtn:(BOOL)selBtnSelectState andIndexPath:(NSIndexPath *)indexPath{
        BOOL selected = !selBtnSelectState;
        
        //获取模型
        QHLShop *shop = self.shoppingCar[indexPath.section];
        QHLGoods *good = shop.goods[indexPath.row];
        
        
        //设置按钮所在的cell的按钮的选中状态
        good.selected = selected;
        
        if (self.state == QHLViewStateNormal) { //判断当前view的state
            
            if (selected) {
                self.count ++;
                
                //添加金额
                self.money += [good.price integerValue];
                
            }else {
                self.count --;
                
                //减去金额
                self.money -= [good.price integerValue];
            }
            
            self.settleMentView.count = self.count;
            self.settleMentView.money = self.money;
            
        } else { //edit 状态
            
            //获取沙盒数组中的模型
            QHLShop *shops = self.tempArray[indexPath.section];
            QHLGoods *goods = shops.goods[indexPath.row];
            
            [self handleObjectInArrays:good documentsObject:goods selectedState:selected];
            
        }
        
        
        
        //设置按钮cell所在的表头视图的按钮状态
        for (QHLGoods *goods in shop.goods) {
            
            if (!goods.selected) {
                
                shop.selected = NO;
                [self.tableView reloadData];
                
                if (self.state == QHLViewStateNormal) {
                    
                    self.settleMentView.btnSelected = NO; //全选按钮设置为未选中状态
                    
                } else {
                    
                    //获取沙盒数组中的模型
                    QHLShop *shops = self.tempArray[indexPath.section];
                    
                    [self.btnsArray removeObject:shop];
                    [self.documentArray removeObject:shops];
                    
                    self.hiddenView.allSelBtnSelected = NO;  //全选按钮设置为未选中状态
                }
                return;
            }
        }
        //能执行到此处,就说明该组cell的按钮 都是选中状态
        shop.selected = YES;
        if (self.state == QHLViewStateEdited) {
            //获取沙盒数组中的模型
            QHLShop *shops = self.tempArray[indexPath.section];
            
            [self.btnsArray addObject:shop];
            [self.documentArray addObject:shops];
        }
        
        [self.tableView reloadData];
        
        //根据表透视图的按钮状态设置全选按钮的状态
        for (QHLShop *shop in self.shoppingCar) {
            if (!shop.selected) {
                
                [self setButtonSelectState:NO];
                
                return;
            }
        }
        [self setButtonSelectState:YES];
    }

    在该自定义cell的button点击的代理方法中:
    1. 当点击cell中的btn的时候,先判断当前购物车的state是 QHLViewStateNormal 还是 QHLViewStateEdited!!

    当state是 QHLViewStateNormal 的时候:
        对self.count 和self.money 作 增加 或者 减少 的操作.

    当state是 QHLViewStateEdited 的时候:
        调用方法 : [self handleObjectInArrays:good documentsObject:goods selectedState:selected]
        1> 当selected == YES时,把当前点击的cell相对应的self.shoppingCar中的模型对象(good) 保存到self.btnsArray中 或者 从self.btnsArray中移除
        2> 当selected == NO时,把当前点击的cell相对应的self.tempArray中的模型对象(goods)  保存到self.documentArray中 或者 从self.documentArray中移除

    2. 之后遍历该cell所在的组的模型数据数组:
    如果cell所在组的模型数组中的数据有 good.selected是 NO 的,就设置该组组头视图中的 btn 状态为未选中并直接 return; 退出该方法;如果所有good.selected 都是YES,那么设置该组组头视图中的 btn 的状态为选中,并刷新数据.

        2.1> 在该组cell所在的模型数据数组中有 good.selected 是NO 的情况时:
    当state是 QHLViewStateNormal 的时候:
        设置self.settleMentView.btnSelected = NO,  设置后会在相对应的setter方法中来设置对应的全选按钮状态为未选中.

    当state是 QHLViewStateEdited 的时候,在return之前执行下面操作:
        1. 设置self.hiddenView.allSelBtnSelected = NO,  设置后悔在相对应的setter方法中来设置相应的全选按钮的状态为未选中.
        2. 把当前点击的cell在self.shoppingCar中所对应的组头的模型对象(shop) 从self.btnsArray中移除
        3. 把当前点击的cell在self.tempArray中所对应的组头的模型对象(shops) 从self.documentArray中移除

        2.2> 在该组cell所在的模型数据中所有 good.selected 都是 YES 的情况时:
            1. 设置该组cell所在组相对应的组头模型中的selected为YES,并刷新tableView.
            2. 当state是 QHLViewStateEdited 的时候:
                把该组头视图在 self.shoppingCar中所对应的模型对象(shop)  保存到 self.btnsArray中.
                把该组头视图在 self.tempArray中所对应的模型对象(shops)  保存到 self.documentArray中.


        2.3> 之后在遍历整个self.shoppingCar中的每个QHLShop对象,当存在有shop对象的selected 是NO的情况时,根据不同的state状态 设置不同的view的选中属性为NO,并且直接 return 退出方法;当所有shop对象的selected 都是YES时,根据不同是state状态 设置不同的view的选中属性为YES.

    三.  在自定义的组头视图中,视图中的选中button的点击事件通过代理放在控制器中去处理:
    在QHLShoppingCarController.m中实现headerView的代理方法

    #pragma mark - headerView的代理方法
    - (void)headerView:(QHLHeaderView *)headerView selBtnDidClickToChangeAllSelBtn:(BOOL)selBtnSelectState andSection:(NSInteger)section {
        
        BOOL selected = !selBtnSelectState;
        
        //获取对应的模型
        QHLShop *shop = self.shoppingCar[section];
        
        if (self.state == QHLViewStateNormal) {
            
            if (selected) {
                for (QHLGoods *good in shop.goods) {
                    //判断表头所在的cell组中  cell中的按钮 是否选中,当不选中的情况下 执行下面代码
                    if (!good.selected) {
                        self.count ++;
                        //添加金额
                        self.money += [good.price integerValue];
                    }
                }
            } else { //这边不用做判断,表头视图中的cell中的按钮 都是选中状态
                for (QHLGoods *good in shop.goods) {
                    self.count --;
                    //添加金额
                    self.money -= [good.price integerValue];
                }
            }
            
            self.settleMentView.count = self.count;
            self.settleMentView.money = self.money;
            
        } else {  //edit 状态
            QHLShop *shops = self.tempArray[section];
            
            if (selected) { //组头视图中的button选中时
                [self.documentArray addObject:shops];
                [self.btnsArray addObject:shop];
            } else { // 组头视图中的button取消选中时
                [self.documentArray removeObject:shops];
                [self.btnsArray removeObject:shop];
            }
            
            NSInteger count = shop.goods.count;
            for (int i = 0; i < count; i++) {
                
                if (selected) {
                    [self.documentArray addObject:shops.goods[i]];
                    [self.btnsArray addObject:shop.goods[i]];
                } else {
                    [self.documentArray removeObject:shops.goods[i]];
                    [self.btnsArray removeObject:shop.goods[i]];
                }
            }
        }
        
        //设置表头view的按钮状态
        shop.selected = selected;
        
        //设置表头所在组cell的按钮状态
        for (QHLGoods *good in shop.goods) {
            good.selected = selected;
        }
        [self.tableView reloadData];
        
        
        //设置全选按钮的选中状态
        for (QHLShop *shop in self.shoppingCar) {
            if (!shop.selected) {
                
                if (self.state == QHLViewStateNormal) { //根据状态来设置不同的全选按钮的选中
                    self.settleMentView.btnSelected = NO;
                } else {
                    self.hiddenView.allSelBtnSelected = NO;
                }
                
                return;
            }
        }
        if (self.state == QHLViewStateNormal) { //根据状态来设置不同的全选按钮的选中
            self.settleMentView.btnSelected = YES;
        } else {
            self.hiddenView.allSelBtnSelected = YES;
        }
    }

    在该自定义headerView的删除button点击的代理方法中:

    1. 当点击cell中的btn的时候,先判断当前购物车的state是 QHLViewStateNormal 还是 QHLViewStateEdited!!

    当 当前的state 是QHLViewStateNormal的时候:
        1> 如果headerView中的button状态为选中时候,遍历当前headerView所在组相对应的shop.goods模型数组:
            1> 遍历时,当模型数组中的good.selected == NO 时, 对 self.count 和 self.money 自加;如果good.selected == YES 时,不做处理

        2> 如果headerView中的button状态为未选中时候,遍历当前headerView所在组相对应的shop.goods模型数组:
            1> 遍历时,对self.count 和 self.money 做自减操作

    当 当前的state 是QHLViewStateEdited的时候:
        1> 调用方法 : [self handleObjectInArrays:shop documentsObject:shops selectedState:selected] 根据 headerView中的button选中状态selected来添加或者移除self.btnsArray 和 self.documentArray 中 对应的QHLShop 模型对象

        2> 根据当前headerView所在的组的cell个数 做循环, 在循环中调用方法 : [self handleObjectInArrays:shop.goods[i] documentsObject:shops.goods[i] selectedState:selected]  根据 headerView中的button选中状态selected来添加或者移除self.btnsArray 和 self.documentArray 中 对应的QHLGoods 模型对象

    2. 对 shop.goods 模型数组做循环遍历:
        根据当前shop.selected 的 bool 值 来设置 good.selected 的值(即 good.selected == shop.selected)

    3. 对 self.shoppingCar 模型数组做循环遍历:
        1> 当该数组中 至少有一个shop.selected == NO的时候,直接return 退出该方法;
           在return之前 调用方法 : [self setButtonSelectState:NO] 根据不同的state状态来设置不同的view中的全选按钮的隐藏

        2> 当所有的shop.selected == YES 的时候,遍历结束之后,调用方法 : [self setButtonSelectState:YES] 根据不同的state状态来设置不同的view中的全选按钮的隐藏

    四.  在该自定义headerView的全选按钮的点击事件的代理方法中:
    遍历self.shoppingCar模型数组,根据全选按钮的不同的选中状态,调用 handleObjectInArrays: documentsObject: selectedState: 方法来对self.documentArray 和 self.btnsArray 数组 做不同的操作

    五.  在右上角编辑按钮的点击事件中:
    - (void)editBtnDidClick:(QHLButton *)editBtn方法中

    设置底部2个view的隐藏显示切换
    改变编辑按钮的选中状态

    1> 当编辑按钮选中时:
        1. 改变self.state  变为 编辑状态
        2. 保存self.shoppingCar模型数组,归档写入到应用沙盒中
        3. 遍历self.shoppingCar 模型数组,是其中所有的模型的selected属性为 NO,最后刷新tableView

    2> 当编辑按钮取消选中时:
        1. 改变self.state 变为 正常状态
        2. 设置隐藏view的全选按钮的选中属性为NO
        3. 把self.tempArray(存储在沙盒中的模型数据数组) 赋值给 self.shoppingCar,然后刷新tableView
        4. 把self.btnsArray self.documentArray self.tempArray 置为 nil
        5. 判断self.shoppingCar中元素的个数,如果个数为0 设置结算view中的全选按钮的选中状态为NO,并return 退出方法;如果self.shoppingCar.count不为0的话执行下面操作
        6. 先把 self.count 和 self.money 的值 等于0!!!然后遍历self.shoppingCar模型数组,根据QHLGoods模型对象good的selected 是否等于 YES 来使self.count 和 self.money 执行自加操作
        7. 遍历self.shoppingCar中的shop.goods模型数组:
            1> 当数组中有QHLGoods模型对象的selected的值是NO的话,break跳出本次循环.在break之前设置该对象所在的shop.selected为NO,和结算view的全选按钮的状态为NO
            2> 当shop.goods中的good.selected都是YES的时候,shop.selected 设置为YES.
        8.遍历self.shoppingCar模型数组:
            1> 当shop.selected 的值 有为NO时,设置结算view中的全选按钮的状态为未选中,并且return退出方法.
            2> 当所有shop.selected的值 都是YES时,设置结算view中的全选按钮的状态为选中.

    六.  抽取的用来遍历数组删除数据的方法

    /**
     *  @param dataArray  dataArray 保存要删除的对象的数组
     *  @param modelArray modelArray 模型数组 要从该数组中删除数据
     */
    #pragma mark - 遍历删除模型数组dataArray中的元素在模型数组modelArray中对应的object
    - (void)enumerateObjectsUsForinWithDataArray:(NSMutableArray *)dataArray modelArray:(NSMutableArray *)modelArray {
        
        //遍历删除 对应的good
        for (id obj in dataArray) {
            
            for (QHLShop *shops in modelArray) {
                
                for (QHLGoods *goods in shops.goods) {
                    
                    if ([goods isEqual:obj]) {
                        
                        [shops.goods removeObject:goods];
                        break;
                        
                    }
                }
            }
        }
        
        //遍历删除 对应的shops
        for (id obj in dataArray) {
            
            for (QHLShop *shops in modelArray) {
                
                if ([shops isEqual:obj]) {
                    
                    [modelArray removeObject:shops];
                    break;
                }
            }
        }
    }

    七.  编辑状态下,在删除按钮的点击事件中:

    #pragma mark - hiddenView delegate
    - (void)hiddenView:(QHLHiddenView *)hiddenView didClicHiddenViewBtn:(QHLHiddenViewButtonState)buttonType {
        if (buttonType == QHLHiddenViewButtonStateShared) {
            
        } else if (buttonType == QHLHiddenViewButtonStateAttention) {
            
        } else if (buttonType == QHLHiddenViewButtonStateDelete) {
            
            //修改self.shoppingCar
            [self enumerateObjectsUsForinWithDataArray:self.btnsArray modelArray:self.shoppingCar];
            
            [self.tableView reloadData];
            
            //修改保存沙盒数据的数值中对应的数据
            [self enumerateObjectsUsForinWithDataArray:self.documentArray modelArray:self.tempArray];
            
            if (!self.shoppingCar.count) {
                self.hiddenView.allSelBtnSelected = NO;
            }
        }
    }

    在按钮的删除方法中,先遍历删除QHLGoods对象模型,在遍历QHLShop对象模型,根据self.btnsArray 和 self.documentArray 中保存的元素 在 self.shoppingCar 和 self.tempArray 中 删除相对应的模型对象.遍历结束后,判断self.shoppingCar中是否还有元素,没有元素了的话设置全选按钮为未选中状态

    八.  在添加cell侧滑删除功能的方法中:

    #pragma mark - 设置cell侧滑按钮
    - (nullable NSArray<UITableViewRowAction *> *)tableView:(UITableView *)tableView editActionsForRowAtIndexPath:(NSIndexPath *)indexPath {
        
        QHLShop *shop = self.shoppingCar[indexPath.section];
        
        //删除
        UITableViewRowAction *deleteAction = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleDefault title:@"删除" handler:^(UITableViewRowAction * _Nonnull action, NSIndexPath * _Nonnull indexPath) {
            //删除数据
            [shop.goods removeObjectAtIndex:indexPath.row];
            
            
            if (!shop.goods.count) {   //判断 shoppingCar数组中的shop对象的goods数组 是否为空  为空的话移除shop  不为空的话 移除good
                [self.shoppingCar removeObjectAtIndex:indexPath.section];
            }
            [tableView reloadData];
            
            
            if (self.state == QHLViewStateEdited) { //在编辑界面下删除cell时  同时删除沙盒中读取到的数组中对应的元素
                //根据indexPath获取到QHLShop对象
                QHLShop *shops = self.tempArray[indexPath.section];
                
                //移除该对象goods数组中的下标为indexPath.row的元素
                [shops.goods removeObject:shops.goods[indexPath.row]];
                
                if (!shops.goods.count) { //判断沙盒存储的数组中的shop对象的goods数组 是否为空  为空的话移除
                    [self.tempArray removeObject:shops];
                }
            }
            
            for (QHLGoods *good in shop.goods) {
                if (!good.selected) {
                    shop.selected = NO;
                    [self setButtonSelectState:NO];
                    [tableView reloadData];
                    return;
                }
            }
            shop.selected = YES;
            if (self.state == QHLViewStateEdited) {
                //根据indexPath获取到QHLShop对象
                QHLShop *shops = self.tempArray[indexPath.section];
                [self handleObjectInArrays:shop documentsObject:shops selectedState:YES];
            }
            [tableView reloadData];
            
            for (QHLShop *shop in self.shoppingCar) {
                if (!shop.selected) {
                    [self setButtonSelectState:NO];
                    return;
                }
            }
            [self setButtonSelectState:YES];
            
        }];
        
        return @[deleteAction];
    }

    在删除按钮的回调方法中:
        1> 当删除按钮被点击时,会先把对应的QHLGoods模型对象从shop.goods中删除掉,然后判断shop.goods中是否还有别的元素,没有的话删从self.shoppingCar中删除掉shop模型对象,然后刷新tableView.
        2> 然后判断当前self.state是否是编辑状态,是的话,根据indexPath在模型数组self.tempArray(存储在沙盒中的模型数组)中删除相对应的goods模型对象,然后判断该模型对象所在的shops.goods是否还有别的元素,没有了的话把shops模型对象从self.tempArray中移除掉.
        3> 遍历删除的cell所在的组模型数组shop.goods模型数组,当有good.selected == NO时,设置shop.selected = NO 并且根据不同的state状态 设置不同的底部view的全选按钮的选中状态,最后return结束回调方法
        4> 当遍历结束时,说明shop.goods中所有的good.selected 都是YES,所以设置shop.selected = YES,如果当前的state是在编辑状态下的时候,把对应的shop模型对象和shops模型对象保存到对应的数组中,并且刷新tableView.
        5> 遍历self.shoppingCar模型数组,如果有shop.selected = NO的话,调用[self setButtonSelectState:NO]方法 根据不同的strate来设置不同的全选按钮为未选中状态,并且退出回调方法;如果所有的shop.selected 都是YES的话,调用[self setButtonSelectState:NO]方法 根据不同的strate来设置不同的全选按钮为选中状态.

    通过这些主要的方法的实现以及tableView的 数据源 和 代理 方法的实现,差不多就可以实现购物车的基本功能了.

    demo已经上传到cocoachina那边了 连接:http://code.cocoachina.com/view/129721

  • 相关阅读:
    三级菜单python写法(递归写法)
    webstorm2018.1.6版本安装+破解+汉化
    sourceTree 的使用教程
    nodeppt的使用教程
    堆和栈的区别(转过无数次的文章)
    黎曼滤波在神经计算方面的应用
    深度学习笔记——PCA原理与数学推倒详解
    TCP/IP模型详解
    OSI7层模型详解
    CNN车型分类总结
  • 原文地址:https://www.cnblogs.com/qhlbk/p/5236862.html
Copyright © 2011-2022 走看看