IOS详解TableView——选项抽屉(天猫商品列表)

在之前的有篇文章讲述了利用HeaderView来写类似QQ好友列表的表视图。

这里写的天猫抽屉其实也可以用该方法实现,具体到细节每个人也有所不同。这里采用的是点击cell对cell进行运动处理以展开“抽屉”。

最后完成的效果大概是这个样子。

 

主要的环节:

点击将可视的Cell动画弹开。

其他的Cell覆盖一层半透明视图,将视线焦点集中在弹出来的商品细分类别中。

再次点击选中的或其他Cell,动画恢复到点击之前所在的位置。

商品细分类别属于之前写过的九宫格实现。这里就不贴代码了。之前的文章:点击打开链接

 

这里的素材都来自之前版本天猫的IPA。

加载数据

 

 

[cpp][/cpp] view plaincopy

  1. – (void)loadData
  2. {
  3.     NSString *path = [[NSBundle mainBundle] pathForResource:@”shops” ofType:@”plist”];
  4.     NSArray *array = [NSArray arrayWithContentsOfFile:path];
  5.     NSMutableArray *arrayM = [NSMutableArray arrayWithCapacity:array.count];
  6.     [array enumerateObjectsUsingBlock:^(NSDictionary *dict, NSUInteger idx, BOOL *stop) {
  7.         ProductType *proType = [[ProductType alloc] init];
  8.         proType.name = dict[@”name”];
  9.         proType.imageName = dict[@”imageName”];
  10.         proType.subProductList = dict[@”subClass”];
  11.         [arrayM addObject:proType];
  12.     }];
  13.     self.typeList = arrayM;
  14. }

 

一个ProductType数据模型,记录名称,图片名称等。

 

单元格数据源方法

 

[cpp][/cpp] view plaincopy

  1. – (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
  2. {
  3.     TypeCell *cell = [tableView dequeueReusableCellWithIdentifier:RTypeCellIdentifier];
  4.     [cell bindProductKind:_typeList[indexPath.row]];
  5.     return cell;
  6. }

将数据模型的信息绑定到自定义类中进行处理,这个类在加载视图之后由tableview进行了注册。

 

 

下面看看自定义单元格中的代码

初始化

 

[cpp][/cpp] view plaincopy

  1. – (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
  2. {
  3.     self = [super initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:reuseIdentifier];
  4.     if (self) {
  5.         self.contentView.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@”tmall_bg_main”]];
  6.         //设置clear可以看到背景,否则会出现一个矩形框
  7.         self.textLabel.backgroundColor = [UIColor clearColor];
  8.         self.detailTextLabel.backgroundColor = [UIColor clearColor];
  9.         self.selectionStyle = UITableViewCellSelectionStyleNone;
  10.         //coverView 用于遮盖单元格,在点击的时候可以改变其alpha值来显示遮盖效果
  11.         _coverView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, RScreenWidth, RTypeCellHeight)];
  12.         _coverView.backgroundColor = [UIColor whiteColor];
  13.         _coverView.alpha = 0.0;
  14.         [self addSubview:_coverView];
  15.     }
  16.     return self;
  17. }

 

 

绑定数据

 

[cpp][/cpp] view plaincopy

  1. – (void)bindProductKind:(ProductType *)productType
  2. {
  3.     self.imageView.image = [UIImage imageNamed:productType.imageName];
  4.     self.textLabel.text = productType.name;
  5.     NSArray *array = productType.subProductList;
  6.     NSMutableString *detail = [NSMutableString string];
  7.     [array enumerateObjectsUsingBlock:^(NSDictionary *dict, NSUInteger idx, BOOL *stop) {
  8.         NSString *string;
  9.         if (idx < 2)
  10.         {
  11.             string = dict[@”name”];
  12.             [detail appendFormat:@”%@/”, string];
  13.         }
  14.         else if (idx == 2)
  15.         {
  16.             string = dict[@”name”];
  17.             [detail appendFormat:@”%@”, string];
  18.         }
  19.         else
  20.         {
  21.             *stop = YES;
  22.         }
  23.     }];
  24.     self.detailTextLabel.text = detail;
  25. }

遍历array然后进行判断,对string进行拼接然后显示到细节label上。

 

 

然后是对点击单元格事件的响应处理,处理过程会稍微复杂一点

 

 

[cpp][/cpp] view plaincopy

  1. – (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
  2. {
  3.     if (!_animationCells)
  4.     {
  5.         _animationCells = [NSMutableArray array];
  6.     }
  7.     if (!_open)
  8.     {
  9.         [self openTableView:tableView withSelectIndexPath:indexPath];
  10.     }
  11.     else
  12.     {
  13.         [self closeTableView:tableView withSelectIndexPath:indexPath];
  14.     }
  15. }

_animationCells用于之后记录运动的单元格,以便进行恢复。

 

 

 

[cpp][/cpp] view plaincopy

  1. – (CGFloat)offsetBottomYInTableView:(UITableView *)tableView withIndexPath:(NSIndexPath *)indexPath
  2. {
  3.     UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
  4.     CGFloat screenHeight = RScreenHeight – RNaviBarHeight;
  5.     CGFloat cellHeight = RTypeCellHeight;
  6.     CGFloat frameY = cell.frame.origin.y;
  7.     CGFloat offY = self.tableView.contentOffset.y;
  8.     CGFloat bottomY = screenHeight – (frameY – offY) – cellHeight;
  9.     return bottomY;
  10. }

一个私有方法,为了方便之后获取偏移的高度,这个高度记录点击的单元格的高度到屏幕底部的距离。以便进行判断。

 

 

比如我们假设弹出的抽屉视图高度为200,那么如果点击的单元格到底部的距离超过200,则点击的单元格以及以上的不用向上偏移,只要将下面的单元格向下移动即可。

但是如果距离小于200,则所有单元格都要进行响应的移动才能给抽屉视图腾出空间。

 

按照思路进行开闭操作

 

[cpp][/cpp] view plaincopy

  1. – (void)openTableView:(UITableView *)tableView withSelectIndexPath:(NSIndexPath *)indexPath
  2. {
  3.     /******获取可见的IndexPath******/
  4.     NSArray *paths = [tableView indexPathsForVisibleRows];
  5.     CGFloat bottomY = [self offsetBottomYInTableView:tableView withIndexPath:indexPath];
  6.     if (bottomY >= RFolderViewHeight)
  7.     {
  8.         _down = RFolderViewHeight;
  9.         [paths enumerateObjectsUsingBlock:^(NSIndexPath *path, NSUInteger idx, BOOL *stop) {
  10.             TypeCell *moveCell = (TypeCell *)[tableView cellForRowAtIndexPath:path];
  11.             if (path.row > indexPath.row)
  12.             {
  13.                 [self animateCell:moveCell WithDirection:RMoveDown distance:_down andStatus:YES];
  14.                 [_animationCells addObject:moveCell];
  15.             }
  16.             if (path.row != indexPath.row)
  17.             {
  18.                 //遮盖视图改变透明度 让其他单元格变暗
  19.                 moveCell.coverView.alpha = RCoverAlpha;
  20.             }
  21.         }];
  22.     }
  23.     else
  24.     {
  25.         _up = RFolderViewHeight – bottomY;
  26.         _down = bottomY;
  27.         [paths enumerateObjectsUsingBlock:^(NSIndexPath *path, NSUInteger idx, BOOL *stop) {
  28.             TypeCell *moveCell = (TypeCell *)[tableView cellForRowAtIndexPath:path];
  29.             if (path.row != indexPath.row)
  30.             {
  31.                 moveCell.coverView.alpha = RCoverAlpha;
  32.             }
  33.             if (path.row <= indexPath.row)
  34.             {
  35.                 [self animateCell:moveCell WithDirection:RMoveUp distance:_up andStatus:YES];
  36.             }
  37.             else
  38.             {
  39.                 [self animateCell:moveCell WithDirection:RMoveDown distance:_down andStatus:YES];
  40.             }
  41.             [_animationCells addObject:moveCell];
  42.         }];
  43.     }
  44.     //禁止滚动表格视图
  45.     tableView.scrollEnabled = NO;
  46. }

主要对可视的单元格进行了判断移动,

 

其中[self animateCell:moveCell WithDirection:RMoveDown distance:_down andStatus:YES];是一个私有的重构后的方法。

不过一般情况下,动画的方法尽量在所有需求完成后再进行重构,因为毕竟不同的情况可能处理会很不同(动画方式,动画后的处理),放到一个方法后之后可能会发生需要再改回去。

看下这个方法

 

[cpp][/cpp] view plaincopy

  1. – (void)animateCell:(TypeCell *)cell WithDirection:(RMoveDirection)direction distance:(CGFloat)dis andStatus:(BOOL)status
  2. {
  3.     CGRect newFrame = cell.frame;
  4.     cell.direction = direction;
  5.     switch (direction)
  6.     {
  7.         case RMoveUp:
  8.             newFrame.origin.y -= dis;
  9.             break;
  10.         case RMoveDown:
  11.             newFrame.origin.y += dis;
  12.             break;
  13.         default:NSAssert(NO, @”无法识别的方向”);
  14.             break;
  15.     }
  16.     [UIView animateWithDuration:RCellMoveDuration
  17.                      animations:^{
  18.                          cell.frame = newFrame;
  19.                      } completion:^(BOOL finished) {
  20.                          _open = status;
  21.                      }];
  22. }

传入参数为单元格,动画方向,运动的距离以及一个判断是否打开的标识位。

 

 

最后看下闭合操作

 

[cpp][/cpp] view plaincopy

  1. – (void)closeTableView:(UITableView *)tableView withSelectIndexPath:(NSIndexPath *)indexPath
  2. {
  3.     [_animationCells enumerateObjectsUsingBlock:^(TypeCell *moveCell, NSUInteger idx, BOOL *stop) {
  4.         if (moveCell.direction == RMoveUp)
  5.         {
  6.             [self animateCell:moveCell WithDirection:RMoveDown distance:_up andStatus:NO];
  7.         }
  8.         else
  9.         {
  10.             [self animateCell:moveCell WithDirection:RMoveUp distance:_down andStatus:NO];
  11.         }
  12.     }];
  13.     NSArray *paths = [tableView indexPathsForVisibleRows];
  14.     for (NSIndexPath *path in paths)
  15.     {
  16.         TypeCell *typeCell = (TypeCell *)[tableView cellForRowAtIndexPath:path];
  17.         typeCell.coverView.alpha = 0;
  18.     }
  19.     _up = 0;   //对一系列成员进行处理。
  20.     _down = 0;
  21.     tableView.scrollEnabled = YES;
  22.     [_animationCells removeAllObjects];
  23. }

 

 

Demo源码:点击打开链接

 

不过这个素材来自于之前天猫客户端的版本,现在的天猫客户端对商品列表进行了改变。也是弹出,不过弹出的列表内容更多,占据了整个屏幕。

标签