两个代理方法实现iOS购物车1

图片 1gif图片.gif

图片 2首页图.png

最近项目中要用到tableview 插入 删除 移动的功能,以前开发的时候没用到这类的工能,基本上时从零开始,期间遇到不少坑

没有用到通知.只要两个代理方法实现购物车

下面我们一步一步来看看这个功能怎么实现的1,插入功能 简单 刚开始 初始化一个数据源的可变数组,在点击添加内容按钮的时候 在点击事件里面为数据源数组添加数据然后你可以relodata 然后功能就实现啦

github的地址:

 if (sender.tag==501) { NSMutableDictionary *tempDict = [NSMutableDictionary dictionary]; [tempDict setObject:@"0" forKey:@"type"]; [self.dataSouce addObject:tempDict]; [self.data_souce addObject:tempDict]; [comTableView reloadData]; NSLog(@"昂起值%@",self.dataSouce); }

图片 3购物车.gif

因为我点击点击添加按钮的时候的 是分添加文字或者图片俩种,所以我在给数据源添加了一个字典进去,type值 就是分开哪个是文本哪个是图片,

1.第三方:用到的框架SDWebImage/MJExtension,应该都很熟悉,第一个是图片下载,第二个是字典转模型.

在reloadData的时候 你会遇到复用问题,这里就不阐述 复用的概念啦,就是你在reloaddata的时候 你添加到文本上的内容没了,或者你添加的多张图片瞬间变成同一张了,

2.功能: ①勾选总价计算; ②删除功能

解决的办法是,你把添加的文本或者图片存放到字典里然后把刚开始存储在数组中的字典覆盖掉,然后在reloadData的时候把存好的内容在重新赋值回去,cellForRowAtIndexPath里这样写

0.拖入相关文件

 NSDictionary *dict = self.dataSouce[indexPath.row]; if ([dict[@"type"]isEqualToString:@"0"]) { self.cell.comImage.hidden = YES; CreateWeakSelf; self.cell.textViewInputCompletion = ^(NSString *text){ NSMutableDictionary *temp=[[NSMutableDictionary alloc]init]; [temp setObject:@"0" forKey:@"type"]; [temp setObject:text forKey:@"str"]; [weakSelf.dataSouce replaceObjectAtIndex:indexPath.row withObject:temp]; [weakSelf.data_souce replaceObjectAtIndex:indexPath.row withObject:temp]; }; self.cell.comTextView.text =[dict objectForKey:@"str"];
#import "ViewController.h"#import "MKShopCarController.h"#import "MKOrderListModel.h"#import "MJExtension.h"

2,删除这个比较简单 就是删除你原来数据源数组里面先对应的index就行,

1.传递数据

-delegateAction:(UIButton *)sender{ NSLog(@"index的值为%@",sender.additionalMark); NSString *str = [NSString stringWithFormat:@"%@",sender.additionalMark]; NSInteger i = [str integerValue];// NSUInteger i= sender.additionalMark; [self.dataSouce removeObjectAtIndex:i]; [self.data_souce removeObjectAtIndex:i]; [comTableView reloadData];}
// 传递模型数据- shopCarClick { MKShopCarController * shop = [MKShopCarController new]; shop.modelList = _nmArray; [self.navigationController pushViewController:shop animated:YES];}

这里我遇到的问题是,刚开始我是初始化一个 NSIndexpath 然后 在cellforrowindexptath里面 把indexpath赋值给我初始化的NSIndexpath 拿indexpate.row作为删除的数组里index下标的,问题是当你在reloadData的时候,赋值给NSindexpath 永远是最下面cell,所以后来解决办法是 当你点击删除按钮的时候 把当前要铲除cell的 indexpath 赋值,然后问题就解决啦

2.修改模型数据

 self. cell.deleageBtn.additionalMark = [NSString stringWithFormat:@"%ld",indexPath.row];

图片 4修改模型key值.png

3,移动我是直接百度的,具体的没有去深研究,大概就是 给当前的cell添加长按手势,当你触发手势的时候,会把当前的cell进行一个快照,说白了就是你拖到的不是实际的cell,而是照片,当你拖动的时候,然后再把数据源的中的数据进行重新排序,然后在reloadta一下 就ok啦 我把代码直接粘贴吧

@end``@end

#pragma mark 创建cell的快照- customSnapshoFromView:inputView { // 用cell的图层生成UIImage,方便一会显示 UIGraphicsBeginImageContextWithOptions(inputView.bounds.size, NO, 0); [inputView.layer renderInContext:UIGraphicsGetCurrentContext()]; UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); // 自定义这个快照的样子(下面的一些参数可以自己随意设置) UIView *snapshot = [[UIImageView alloc] initWithImage:image]; snapshot.layer.masksToBounds = NO; snapshot.layer.cornerRadius = 0.0; snapshot.layer.shadowOffset = CGSizeMake(-5.0, 0.0); snapshot.layer.shadowRadius = 5.0; snapshot.layer.shadowOpacity = 0.4; return snapshot;}#pragma mark 长按手势方法- longPressGestureRecognized:sender { UILongPressGestureRecognizer *longPress = (UILongPressGestureRecognizer *)sender; UIGestureRecognizerState state = longPress.state; CGPoint location = [longPress locationInView:comTableView]; NSIndexPath *indexPath = [comTableView indexPathForRowAtPoint:location]; static UIView *snapshot = nil; switch  { // 已经开始按下 case UIGestureRecognizerStateBegan: { // 判断是不是按在了cell上面 if (indexPath) { sourceIndexPath = indexPath; UITableViewCell *cell = [comTableView cellForRowAtIndexPath:indexPath]; // 为拖动的cell添加一个快照 snapshot = [self customSnapshoFromView:cell]; // 添加快照至tableView中 __block CGPoint center = cell.center; snapshot.center = center; snapshot.alpha = 0.0; [comTableView addSubview:snapshot]; // 按下的瞬间执行动画 [UIView animateWithDuration:0.25 animations:^{ center.y = location.y; snapshot.center = center; snapshot.transform = CGAffineTransformMakeScale(1.05, 1.05); snapshot.alpha = 0.98; cell.alpha = 0.0; } completion:^(BOOL finished) { cell.hidden = YES; }]; } break; } // 移动过程中 case UIGestureRecognizerStateChanged: { // 这里保持数组里面只有最新的两次触摸点的坐标 [self.touchPoints addObject:[NSValue valueWithCGPoint:location]]; if (self.touchPoints.count > 2) { [self.touchPoints removeObjectAtIndex:0]; } CGPoint center = snapshot.center; // 快照随触摸点y值移动(当然也可以根据触摸点的y轴移动量来移动) center.y = location.y; // 快照随触摸点x值改变量移动 CGPoint Ppoint = [[self.touchPoints firstObject] CGPointValue]; CGPoint Npoint = [[self.touchPoints lastObject] CGPointValue]; CGFloat moveX = Npoint.x - Ppoint.x; center.x += moveX; snapshot.center = center; NSLog(@"%@---%f----%@", self.touchPoints, moveX, NSStringFromCGPoint; NSLog(@"%@", NSStringFromCGRect(snapshot.frame)); // 是否移动了 if (indexPath && ![indexPath isEqual:sourceIndexPath]) { // 更新数组中的内容 [self.dataSouce exchangeObjectAtIndex: indexPath.row withObjectAtIndex:sourceIndexPath.row]; // 把cell移动至指定行 [comTableView moveRowAtIndexPath:sourceIndexPath toIndexPath:indexPath]; // 存储改变后indexPath的值,以便下次比较 sourceIndexPath = indexPath; } break; } // 长按手势取消状态 default: { // 清除操作 // 清空数组,非常重要,不然会发生坐标突变! [self.touchPoints removeAllObjects]; [comTableView reloadData]; UITableViewCell *cell = [comTableView cellForRowAtIndexPath:sourceIndexPath]; cell.hidden = NO; cell.alpha = 0.0; // 将快照恢复到初始状态 [UIView animateWithDuration:0.25 animations:^{ snapshot.center = cell.center; snapshot.transform = CGAffineTransformIdentity; snapshot.alpha = 0.0; cell.alpha = 1.0; } completion:^(BOOL finished) { sourceIndexPath = nil; [snapshot removeFromSuperview]; snapshot = nil; [comTableView reloadData]; }]; break; } } }
  • 1

大概功能差不多已经实现,不过实际开发的时候,还会有许多问题需要考虑,比如修改文本内容的时候,你要实时更新数据源中的内容,返回到上一个界面的时候,在重新进入的时候,你还需要tableview展示你刚才添加的内容以便修改,,,,,期间我遇到的问题很多,还有很多细节问题,我就不一一阐述啦 如果开发中你们需要这样的功能,遇到什么问题可以私信给我,尽力帮助你们最后附上demogithub下载地址不麻烦的话可以 给个 star你们的star 是我进步的动力

有什么问题可以私信我,欢迎叨扰,非诚勿扰 谢谢

// 继承UIViewController@interface MKShopCarController : UIViewController

// 设置表格- setTableList { _tableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain]; [self.view addSubview:_tableView]; _tableView.delegate = self; _tableView.dataSource = self; UINib * nib = [UINib nibWithNibName:@"MKShopCarCell" bundle:nil]; [_tableView registerNib:nib forCellReuseIdentifier:@"shop"]; _tableView.rowHeight = 110; _tableView.contentInset = UIEdgeInsetsMake(0, 0, 49, 0);}

这里创建为UITableViewStylePlain样式组头会停住UITableViewStyleGroup样式组头不会停住,这也是分组样式与否的区别

  • 2.第一个代理方法,自定义cell中,选中按钮的点击事件并且将当前的cell的tag值传递回控制器中
//自定义cell中@interface MKShopCarCell : UITableViewCell

// 自定义cell中左侧选中按钮的点击事件- fabricSelectClick:(UIButton*)sender { _goodsModel.isSelected = !_goodsModel.isSelected; fabricSelectedBtn.selected = !fabricSelectedBtn.selected;// NSLog(@"%zd",self.tag); if ([self.shopDelegate respondsToSelector:@selector(shopCellSelectedClick:)]) { [self.shopDelegate shopCellSelectedClick:self.tag]; }}
  • 3.第二个代理方法,自定义组头中,选中按钮的点击事件中同样将当前section中tag传递
//自定义组头中@interface MKHeaderFooterView : UITableViewHeaderFooterView

// 组头的点击事件- headerBtnClick: (UIButton*)HeaderBtn :(NSInteger)section{ HeaderBtn.selected = !HeaderBtn.selected; if ([self.headerDelegate respondsToSelector:@selector(headerSelectedBtnClick:)]) { [self.headerDelegate headerSelectedBtnClick:self.tag]; }}
  • 4.控制器中,遵守协议,实现这两个代理方法
<UITableViewDelegate,UITableViewDataSource,shopCarCellDelegate,headerViewDelegate>

// 注册cell/header 或者判断为空的时候 创建 UINib * nib = [UINib nibWithNibName:@"MKShopCarCell" bundle:nil]; [_tableView registerNib:nib forCellReuseIdentifier:@"shop"];
    • 组头的数据方法 | 绑定tag
- tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section { _headerView = [tableView dequeueReusableHeaderFooterViewWithIdentifier:@"MKShopCarHeader"]; if (_headerView == nil) { _headerView = [[MKHeaderFooterView alloc] init]; _headerView.headerDelegate = self; } MKOrderListModel*listModel = _modelList[section]; _headerView.tag = section; _headerView.headerBtn.selected = listModel.groupSelected; return _headerView;}
    • cell的数据源方法 | 绑定tag
// cell显示内容- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { MKShopCarCell*shopCell = [tableView dequeueReusableCellWithIdentifier:@"shop"]; shopCell.shopDelegate = self; //取出对应组的对应商品信息 shopCell.goodsModel = ((MKOrderListModel*)_modelList[indexPath.section]).goods[indexPath.row]; // 给cell做标记 shopCell.tag = indexPath.section *100 + indexPath.row;// if (_modelList.count != self.dic.count) { NSString * cellTag = [NSString stringWithFormat:@"%zd",shopCell.tag]; NSDictionary* _tempDic = @{ cellTag:indexPath }; [self.dic addEntriesFromDictionary:_tempDic];// } return shopCell;}

这里将所有的indexpath存入一个字典里,并通过对应的tag作为key值为保证唯一性section和row都做了处理作为key

    • 实现cell中的代理方法 | 计算选中与未选中的总价
#pragma mark - cell上的代理方法获 | 取的价格- shopCellSelectedClick:(NSInteger)shopCellTag { //判断组的是否选中状态是否修改 NSString * cellTagStr = [NSString stringWithFormat:@"%zd",shopCellTag]; NSIndexPath *indexPath = self.dic[cellTagStr]; MKOrderListModel * listModel = (MKOrderListModel*)_modelList[indexPath.section]; //0.便利当前组cell上选中按钮的个数 NSInteger seletedNum =0; for (MKGoodsModel* goodsModel in listModel.goods) { if (goodsModel.isSelected) { seletedNum += 1; } // 1.当前组的cell的个数 是否等于 勾选的总数 if (((MKOrderListModel*)_modelList[indexPath.section]).goods.count == seletedNum) { listModel.groupSelected = YES; } else { listModel.groupSelected = NO; } [_tableView reloadData]; } MKGoodsModel *goodsModel = ((MKOrderListModel*)_modelList[indexPath.section]).goods[indexPath.row]; float shop_price = goodsModel.shop_price; float goods_number = goodsModel.goods_number; if (!goodsModel.isSelected) { _totalNum = _totalNum - shop_price*goods_number; }else { _totalNum = _totalNum + shop_price*goods_number; } _hejiLabel.text = [NSString stringWithFormat:@"¥%.2f",_totalNum -1 + 1];}

即,根据代理中的tag 取出 indexpath 再根据indexpath 取出模型中的数据另外判断本组中的cell都选中时候组的选中状态也为选中!

    • 实现header中我们写的的代理方法 | 计算选中与未选中的总价
#pragma mark - 代理方法组头header的选中状态- headerSelectedBtnClick:(NSInteger)section { // NSLog(@"%zd",section); MKOrderListModel*listModel = _modelList[section]; listModel.groupSelected = !listModel.groupSelected; // 判断如果点击 | header选中 if (listModel.groupSelected) { for (MKGoodsModel* goodsModel in listModel.goods) { if (!goodsModel.isSelected) { //下面不是选中状态的cell 将价格加入到总价当中 float shop_price = goodsModel.shop_price; //价格 float goods_number = goodsModel.goods_number; // 数量 _totalNum += shop_price * goods_number; goodsModel.isSelected = YES; } } } else { // 取消header选中 所有都取消 for (MKGoodsModel* goodsModel in listModel.goods) { goodsModel.isSelected = NO; float shop_price = goodsModel.shop_price; //价格 float goods_number = goodsModel.goods_number; // 数量 _totalNum -= shop_price * goods_number; } }// NSLog(@"总价格为: %.2f",_totalNum); _hejiLabel.text = [NSString stringWithFormat:@"¥%.2f",_totalNum - 1 + 1]; [_tableView reloadData];}

逻辑分析: 组头点击选中 : 则遍历本组cell 未选中状态的cell计算价格加入到总价更并改cell选中状态组头点击未选中 : 遍历本组中cell 的价格 从总价中减去并更改cell的选中状态最后reloadData

    • 删除方法 | 根据方法中自带的indexpath从数据中删除跟新即可
//左拉抽屉- (NSArray *)tableView:(UITableView *)tableView editActionsForRowAtIndexPath:(NSIndexPath *)indexPath{ // 添加一个删除按钮 UITableViewRowAction *deleteRowAction = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleDestructive title:@"删除"handler:^(UITableViewRowAction *action, NSIndexPath *indexPath) { MKOrderListModel*listModel = _modelList[indexPath.section]; NSMutableArray*goodsModel = (NSMutableArray*)listModel.goods; /// 如果删除的是带勾选的则计算一次数值 MKGoodsModel*goodModel = (MKGoodsModel*)goodsModel[indexPath.row]; if (goodModel.isSelected) { float shop_price = goodModel.shop_price; //价格 float goods_number = goodModel.goods_number; // 数量 _totalNum -= shop_price * goods_number; _hejiLabel.text = [NSString stringWithFormat:@"%.2f",_totalNum]; } [goodsModel removeObjectAtIndex:indexPath.row]; // 删除操作放到最后 [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade]; if (goodsModel.count == 0) { NSMutableArray *temp = [NSMutableArray arrayWithArray:_modelList]; [temp removeObjectAtIndex:indexPath.section]; _modelList = temp; } dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (0.25 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ [tableView reloadData]; }); }]; // 修改资料按钮 UITableViewRowAction *editRowAction = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleDefault title:@"修改"handler:^(UITableViewRowAction *action, NSIndexPath *indexPath) { }]; editRowAction.backgroundColor = [UIColor blueColor]; // 将设置好的按钮放到数组中返回 return @[deleteRowAction, editRowAction];}

注意点 : ①可能遇到删除报错的情况,需要条件到一个临时的可变数组中做删除操作 再把删除后的数组回传②判断删除cell中的选中,如果选中状态则在总价中减去此价格即可③删除操作这里需要增加接口后台数据做删除

纯手打,如果有用希望多多支持!~

两个代理方法实现iOS购物车2-完整版

本文由365bet体育在线官网发布于网络编程,转载请注明出处:两个代理方法实现iOS购物车1

TAG标签:
Ctrl+D 将本页面保存为书签,全面了解最新资讯,方便快捷。