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




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

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

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



    主要的环节:

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

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

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

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


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

    加载数据


    - (void)loadData
    {
        NSString *path = [[NSBundle mainBundle] pathForResource:@"shops" ofType:@"plist"];
        NSArray *array = [NSArray arrayWithContentsOfFile:path];
        
        NSMutableArray *arrayM = [NSMutableArray arrayWithCapacity:array.count];
        [array enumerateObjectsUsingBlock:^(NSDictionary *dict, NSUInteger idx, BOOL *stop) {
            ProductType *proType = [[ProductType alloc] init];
            proType.name = dict[@"name"];
            proType.imageName = dict[@"imageName"];
            proType.subProductList = dict[@"subClass"];
            
            [arrayM addObject:proType];
        }];
        self.typeList = arrayM;
    }


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


    单元格数据源方法

    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        TypeCell *cell = [tableView dequeueReusableCellWithIdentifier:RTypeCellIdentifier];
        [cell bindProductKind:_typeList[indexPath.row]];
        return cell;
    }


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


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

    初始化

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


    绑定数据

    - (void)bindProductKind:(ProductType *)productType
    {
        self.imageView.image = [UIImage imageNamed:productType.imageName];
        self.textLabel.text = productType.name;
        
        NSArray *array = productType.subProductList;
        NSMutableString *detail = [NSMutableString string];
        [array enumerateObjectsUsingBlock:^(NSDictionary *dict, NSUInteger idx, BOOL *stop) {
            NSString *string;
            if (idx < 2)
            {
                string = dict[@"name"];
                [detail appendFormat:@"%@/", string];
            }
            else if (idx == 2)
            {
                string = dict[@"name"];
                [detail appendFormat:@"%@", string];
            }
            else
            {
                *stop = YES;
            }
        }];
        self.detailTextLabel.text = detail;
    }


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


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


    - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
    {
        if (!_animationCells)
        {
            _animationCells = [NSMutableArray array];
        }
        
        if (!_open)
        {
            [self openTableView:tableView withSelectIndexPath:indexPath];
        }
        else
        {
            [self closeTableView:tableView withSelectIndexPath:indexPath];
        }
    }


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


    - (CGFloat)offsetBottomYInTableView:(UITableView *)tableView withIndexPath:(NSIndexPath *)indexPath
    {
        UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
    
        CGFloat screenHeight = RScreenHeight - RNaviBarHeight;
        CGFloat cellHeight = RTypeCellHeight;
        CGFloat frameY = cell.frame.origin.y;
        CGFloat offY = self.tableView.contentOffset.y;
        
        CGFloat bottomY = screenHeight - (frameY - offY) - cellHeight;
        
        return bottomY;
    }


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


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

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


    按照思路进行开闭操作

    - (void)openTableView:(UITableView *)tableView withSelectIndexPath:(NSIndexPath *)indexPath
    {
        /******获取可见的IndexPath******/
        NSArray *paths = [tableView indexPathsForVisibleRows];
        
        CGFloat bottomY = [self offsetBottomYInTableView:tableView withIndexPath:indexPath];
    
        if (bottomY >= RFolderViewHeight)
        {
            _down = RFolderViewHeight;
            [paths enumerateObjectsUsingBlock:^(NSIndexPath *path, NSUInteger idx, BOOL *stop) {
                TypeCell *moveCell = (TypeCell *)[tableView cellForRowAtIndexPath:path];
                if (path.row > indexPath.row)
                {
                    [self animateCell:moveCell WithDirection:RMoveDown distance:_down andStatus:YES];
                    [_animationCells addObject:moveCell];
                }
                if (path.row != indexPath.row)
                {
                    //遮盖视图改变透明度 让其他单元格变暗
                    moveCell.coverView.alpha = RCoverAlpha;
                }
            }];
        }
        else
        {
            _up = RFolderViewHeight - bottomY;
            _down = bottomY;
            [paths enumerateObjectsUsingBlock:^(NSIndexPath *path, NSUInteger idx, BOOL *stop) {
                TypeCell *moveCell = (TypeCell *)[tableView cellForRowAtIndexPath:path];
                if (path.row != indexPath.row)
                {
                    moveCell.coverView.alpha = RCoverAlpha;
                }
                
                if (path.row <= indexPath.row)
                {
                    [self animateCell:moveCell WithDirection:RMoveUp distance:_up andStatus:YES];
                }
                else
                {
                    [self animateCell:moveCell WithDirection:RMoveDown distance:_down andStatus:YES];
                }
                [_animationCells addObject:moveCell];
            }];
        }
        
        //禁止滚动表格视图
        tableView.scrollEnabled = NO;
    }


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

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

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

    看下这个方法

    - (void)animateCell:(TypeCell *)cell WithDirection:(RMoveDirection)direction distance:(CGFloat)dis andStatus:(BOOL)status
    {
        CGRect newFrame = cell.frame;
        cell.direction = direction;
        switch (direction)
        {
            case RMoveUp:
                newFrame.origin.y -= dis;
                break;
            case RMoveDown:
                newFrame.origin.y += dis;
                break;
            default:NSAssert(NO, @"无法识别的方向");
                break;
        }
        
        [UIView animateWithDuration:RCellMoveDuration
                         animations:^{
                             cell.frame = newFrame;
                         } completion:^(BOOL finished) {
                             _open = status;
                         }];
    }


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


    最后看下闭合操作

    - (void)closeTableView:(UITableView *)tableView withSelectIndexPath:(NSIndexPath *)indexPath
    {
        [_animationCells enumerateObjectsUsingBlock:^(TypeCell *moveCell, NSUInteger idx, BOOL *stop) {
            if (moveCell.direction == RMoveUp)
            {
                [self animateCell:moveCell WithDirection:RMoveDown distance:_up andStatus:NO];
            }
            else
            {
                [self animateCell:moveCell WithDirection:RMoveUp distance:_down andStatus:NO];
            }
        }];
        
        NSArray *paths = [tableView indexPathsForVisibleRows];
        for (NSIndexPath *path in paths)
        {
            TypeCell *typeCell = (TypeCell *)[tableView cellForRowAtIndexPath:path];
            typeCell.coverView.alpha = 0;
        }
        
        _up = 0;   //对一系列成员进行处理。
        _down = 0;
        tableView.scrollEnabled = YES;
        [_animationCells removeAllObjects];
    }



    Demo源码:点击打开链接


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



    最近一直在写TableView的博客,常用的大部分都包含到了。

    传送门:

    IOS详解TableView——性能优化及手工绘制UITableViewCell

    IOS详解TableView —— QQ好友列表的实现

    IOS详解TableView——对话聊天布局的实现

    IOS详解TableView——实现九宫格效果

    IOS详解TableView——静态表格使用以及控制器间通讯



    以上就是本篇博客全部内容,欢迎指正和交流。转载注明出处~


  • 相关阅读:
    idea整合SVN以及SVN的使用
    解决svn检出后不显示图标的问题
    使用Nexus搭建Maven私服
    Idea中使用Maven
    初识Maven
    外网映射
    php 获取远程图片
    PHP中$_POST,$_GET,$_REQUEST,$_FILES全局变量的全局指什么
    linux定时执行文件
    PHP二维码生成的方法(google APi,PHP类库,libqrencode等)
  • 原文地址:https://www.cnblogs.com/pangblog/p/3341683.html
Copyright © 2020-2023  润新知