zoukankan      html  css  js  c++  java
  • iOS仿京东分类菜单实例实现

    在APP开发过程中此功能还是比较常见的模块,左边为菜单展示,右边为菜单下数据的展示,选择不同的菜单右边的数据源进行更新,此实例主要运用到UITableView,UICollectionView,OC谓词一些知识,结合Masonry进行布局;实现的效果如下:

    涉及的知识点:

    1:UITableView的运用,包含选中与非选中行不同展示,默认行分隔线的色彩跟与左边距离的调整,自动滚动到最顶端的实现等

    2:UICollectionView的运用,包含单元格的加载,及重用时遇到的错乱问题,记录滚动位置的当前值;

    3:数组的运用,包含运用谓词对数组过滤

    一:左边表格单元行的实现内容

    1.1 左边单元行的数据实体,(注意:offsetScorller是为了后面记录右边数据的滚动位置值)

    #import <Foundation/Foundation.h>
    
    @interface leftTagModel : NSObject
    
    //ID值
    @property(assign,nonatomic)long tagID;
    //名称
    @property(copy,nonatomic)NSString *tagName;
    //图标地址(为后期可能带图标做准备)
    @property(copy,nonatomic)NSString *tagImageUrl;
    
    //这个来定位右边数据源滚动的位置
    @property(assign,nonatomic) float offsetScorller;
    @end

    1.2左边单元行的创建(注意:关于行的背影色以及字体显示色彩的修改)

    #import <UIKit/UIKit.h>
    #import "leftTagModel.h"
    
    @interface leftTableCell : UITableViewCell
    
    @property(strong,nonatomic)leftTagModel *curLeftTagModel;
    //是否被选中
    @property(assign,nonatomic)BOOL hasBeenSelected;
    
    @end
    #import "leftTableCell.h"
    
    @interface leftTableCell()
    @property(strong,nonatomic)UIView *leftColorView;
    @property(strong,nonatomic)UILabel *nameLabel;
    @end
    
    //左边色彩条宽度
    static const CGFloat leftColorViewWidth=3;
    //文字字体大小
    static const CGFloat textFontSize=15;
    
    
    
    @implementation leftTableCell
    
    - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
    {
        self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
        if (self) {
            //设置背影色
            self.backgroundColor=[UIColor grayColor];
            self.accessoryType = UITableViewCellAccessoryNone;
            
            if (self.leftColorView==nil) {
                self.leftColorView=[[UIView alloc]init];
                self.leftColorView.backgroundColor=[UIColor blueColor];
                self.leftColorView.hidden=YES;
                [self.contentView addSubview:self.leftColorView];
                [self.leftColorView mas_makeConstraints:^(MASConstraintMaker *make) {
                    make.left.mas_equalTo(self.contentView.mas_left).with.offset(0);
                    make.top.mas_equalTo(self.contentView.mas_top).with.offset(0);
                    make.bottom.mas_equalTo(self.contentView.mas_bottom).with.offset(0);
                    make.width.mas_equalTo(leftColorViewWidth);
                }];
            }
            
            if (self.nameLabel==nil) {
                self.nameLabel=[[UILabel alloc]init];
                self.nameLabel.font=[UIFont systemFontOfSize:textFontSize];
                self.nameLabel.textAlignment=NSTextAlignmentCenter;
                [self.nameLabel sizeToFit];
                [self.contentView addSubview:self.nameLabel];
                [self.nameLabel mas_makeConstraints:^(MASConstraintMaker *make) {
                    make.center.mas_equalTo(self.contentView);
                    make.height.mas_equalTo(@20);
                }];
            }
        }
        return self;
    }
    
    
    - (void)awakeFromNib {
        // Initialization code
    }
    
    - (void)setSelected:(BOOL)selected animated:(BOOL)animated {
        [super setSelected:selected animated:animated];
    
        // Configure the view for the selected state
    }
    
    /**
     *  @author wujunyang, 15-10-10 14:10:52
     *
     *  @brief  设置选中跟没有选中的变化
     *
     *  @param hasBeenSelected 是否被选中
     *
     *  @since <#version number#>
     */
    -(void)setHasBeenSelected:(BOOL)hasBeenSelected
    {
        _hasBeenSelected=hasBeenSelected;
        if (_hasBeenSelected) {
            self.backgroundColor=[UIColor whiteColor];
            self.nameLabel.textColor=[UIColor greenColor];
            self.leftColorView.hidden=NO;
        }
        else
        {
            self.backgroundColor=[UIColor grayColor];
            self.nameLabel.textColor=[UIColor blackColor];
            self.leftColorView.hidden=YES;
        }
    }
    
    -(void)setCurLeftTagModel:(leftTagModel *)curLeftTagModel
    {
        _curLeftTagModel=curLeftTagModel;
        self.nameLabel.text=_curLeftTagModel.tagName;
    }
    
    @end

    二:右边列表的单元格实现

    2.1右边单元格实体(注意tagID是为跟左边的数据源进行关联)

    #import <Foundation/Foundation.h>
    
    @interface noHeadRightModel : NSObject
    
    //实体leftTageModel中的主键值
    @property(assign,nonatomic)long tagID;
    
    @property(assign,nonatomic)long roomID;
    @property(copy,nonatomic)NSString *roomName;
    @property(copy,nonatomic)NSString *roomImageUrl;
    @end

    2.2单元格的实现

    #import <UIKit/UIKit.h>
    #import "noHeadRightModel.h"
    
    @interface rightCollectionViewCell : UICollectionViewCell
    
    @property(strong,nonatomic)noHeadRightModel *curNoHeadRightModel;
    
    +(CGSize)ccellSize;
    
    @end
    #import "rightCollectionViewCell.h"
    
    @interface rightCollectionViewCell()
    @property(strong,nonatomic)UIImageView *roomImageView;
    @property(strong,nonatomic)UILabel *roomLabel;
    @end
    
    static const CGFloat collectionCellHeight=80;
    static const CGFloat labelHeight=20;
    
    @implementation rightCollectionViewCell
    
    //这边很关键 CollectionViewCell重用
    - (id)initWithFrame:(CGRect)frame
    {
        self = [super initWithFrame:frame];
        if (self) {
            if (self.roomImageView==nil) {
                self.roomImageView=[[UIImageView alloc] initWithFrame:CGRectMake(0, 0, ([UIScreen mainScreen].bounds.size.width-80-10*3)/3, collectionCellHeight-labelHeight)];
                self.roomImageView.contentMode=UIViewContentModeScaleAspectFill;
                self.roomImageView.clipsToBounds = YES;
                self.roomImageView.layer.masksToBounds = YES;
                self.roomImageView.layer.cornerRadius = 2.0;
                [self.contentView addSubview:self.roomImageView];
            }
            
            if (self.roomLabel==nil) {
                self.roomLabel=[[UILabel alloc]init];
                self.roomLabel.font=[UIFont systemFontOfSize:15];
                self.roomLabel.textAlignment=NSTextAlignmentCenter;
                [self.roomLabel sizeToFit];
                [self.contentView addSubview:self.roomLabel];
                [self.roomLabel mas_makeConstraints:^(MASConstraintMaker *make) {
                    make.top.mas_equalTo(self.roomImageView.mas_bottom).with.offset(2);
                    make.centerX.mas_equalTo(self.roomImageView).with.offset(0);
                    make.height.mas_equalTo(labelHeight);
                }];
            }
        }
        return self;
    }
    
    -(void)setCurNoHeadRightModel:(noHeadRightModel *)curNoHeadRightModel
    {
        _curNoHeadRightModel=curNoHeadRightModel;
        self.roomImageView.image=[UIImage imageNamed:_curNoHeadRightModel.roomImageUrl];
        self.roomLabel.text=_curNoHeadRightModel.roomName;
    }
    
    
    +(CGSize)ccellSize
    {
        return CGSizeMake(([UIScreen mainScreen].bounds.size.width-80-10*3)/3,collectionCellHeight);
    }
    @end

    注意:在集合UICollectionView重用单元一直出现错乱,加载的数据有问题,是因为没有把布局放在- (id)initWithFrame:(CGRect)frame导致;

    三:主页面的展示内容

    3.1测试数据的构造跟左右两控件的初始化

    #import "menuTableViewController.h"
    
    
    #define kScreenWidth [UIScreen mainScreen].bounds.size.width
    #define kScreenHeight [UIScreen mainScreen].bounds.size.height
    
    @interface menuTableViewController ()<UITableViewDataSource, UITableViewDelegate,UICollectionViewDataSource, UICollectionViewDelegate>
    
    @property (strong, nonatomic) UITableView *myTableView;
    //左边列表的数据源
    @property (nonatomic, strong) NSMutableArray *dataList;
    //右边列表的过滤数据源
    @property(nonatomic,strong)NSMutableArray *rightdataList;
    //右边列表数据源
    @property(nonatomic,strong)NSMutableArray *allRightDataList;
    //当前被选中的ID值
    @property(strong,nonatomic)leftTagModel *curSelectModel;
    //右边列表
    @property (strong, nonatomic) UICollectionView *myCollectionView;
    //是否保持右边滚动时位置
    @property(assign,nonatomic) BOOL isKeepScrollState;
    @property(assign,nonatomic) BOOL isReturnLastOffset;
    @property(assign,nonatomic) NSInteger selectIndex;
    @end
    
    //表格的宽度
    static const CGFloat tableWidthSize=80;
    //行的高度
    static const CGFloat tableCellHeight=44;
    //表格跟集合列表的空隙
    static const CGFloat leftMargin =10;
    
    @implementation menuTableViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        //初始化
        self.view.backgroundColor=[UIColor whiteColor];
        self.dataList=[[NSMutableArray alloc]init];
        self.rightdataList=[[NSMutableArray alloc]init];
        self.allRightDataList=[[NSMutableArray alloc]init];
        self.isReturnLastOffset=YES;
        //是否允许右位保持滚动位置
        self.isKeepScrollState=YES;
        //测试数据
        for (int i=0; i<10; i++) {
            
            //左边列表数据
            leftTagModel *item=[[leftTagModel alloc]init];
            item.tagID=i;
            item.tagName=[NSString stringWithFormat:@"第%d层",i];
            [self.dataList addObject:item];
            
            //右边列表数据
            for (int j=0; j<55; j++) {
                noHeadRightModel *model=[[noHeadRightModel alloc]init];
                model.tagID=i;
                model.roomID=j;
                model.roomName=[NSString stringWithFormat:@"%d层房%d",i,j];
                model.roomImageUrl=[NSString stringWithFormat:@"room%d",j%5];
                [self.allRightDataList addObject:model];
            }
        }
        
        //创建列表
        if (!_myTableView) {
            _myTableView = [[UITableView alloc] initWithFrame:CGRectMake(0,0,tableWidthSize, kScreenHeight) style:UITableViewStylePlain];
            _myTableView.backgroundColor=[UIColor grayColor];
            _myTableView.showsVerticalScrollIndicator = NO;
            _myTableView.showsHorizontalScrollIndicator=NO;
            _myTableView.dataSource = self;
            _myTableView.delegate = self;
            _myTableView.tableFooterView=[[UIView alloc]init];
            _myTableView.separatorColor= [UIColor colorWithRed:52.0f/255.0f green:53.0f/255.0f blue:61.0f/255.0f alpha:1];
            [_myTableView registerClass:[leftTableCell class] forCellReuseIdentifier:NSStringFromClass([leftTableCell class])];
    
            if ([self.myTableView respondsToSelector:@selector(setLayoutMargins:)]) {
                self.myTableView.layoutMargins=UIEdgeInsetsZero;
            }
            if ([self.myTableView respondsToSelector:@selector(setSeparatorInset:)]) {
                self.myTableView.separatorInset=UIEdgeInsetsZero;
            }
            [self.view addSubview:_myTableView];
        }
        
        //创建集合表格
        if (!_myCollectionView) {
            UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
            self.myCollectionView = [[UICollectionView alloc] initWithFrame:CGRectMake(tableWidthSize+leftMargin,64, kScreenWidth-tableWidthSize-2*leftMargin, kScreenHeight) collectionViewLayout:layout];
            self.myCollectionView.backgroundColor=[UIColor whiteColor];
            self.myCollectionView.showsHorizontalScrollIndicator=NO;
            self.myCollectionView.showsVerticalScrollIndicator=NO;
            [self.myCollectionView registerClass:[rightCollectionViewCell class] forCellWithReuseIdentifier:NSStringFromClass([rightCollectionViewCell class])];
            self.myCollectionView.dataSource = self;
            self.myCollectionView.delegate = self;
            [self.view addSubview:self.myCollectionView];
        }
        
        self.selectIndex=0;
        //默认选择第一个
        if (self.dataList.count>0) {
            self.curSelectModel=[self.dataList objectAtIndex:self.selectIndex];
            [self.myTableView selectRowAtIndexPath:[NSIndexPath indexPathForRow:self.selectIndex inSection:0] animated:YES scrollPosition:UITableViewScrollPositionTop];
            [self.myTableView reloadData];
            
            //右边数据加载
            [self predicateDataSoure];
        }
    }
    
    - (void)didReceiveMemoryWarning {
        [super didReceiveMemoryWarning];
    }

    注意:isKeepScrollState是为了让右边是否记录下当前滚动位置,若不想则可以把它设置为NO,它便会自动滚动到Y为0的位置;上面的代码中还包含若列表有数据则默认选中第一个的实现功能,[self predicateDataSoure]则是实现的对数组进行过滤的操作,实用的OC谓词的方式;

    /**
     *  @author wujunyang, 15-10-11 20:10:28
     *
     *  @brief  过滤右边集合的数据
     *
     *  @since <#version number#>
     */
    -(void)predicateDataSoure
    {
        //过滤右边的集合数据
        NSPredicate *pre=[NSPredicate predicateWithFormat:[NSString stringWithFormat:@"tagID=%ld",self.curSelectModel.tagID]];
        self.rightdataList=[[self.allRightDataList filteredArrayUsingPredicate:pre] mutableCopy];
        [self.myCollectionView reloadData];
    }

    3.2表格中相应的UITableViewDataSource, UITableViewDelegate实现内容

    #pragma mark UITableViewDataSource, UITableViewDelegate
    
    -(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
    {
        return self.dataList.count;
    }
    
    - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
        return 1;
    }
    
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
        leftTableCell *cell = [tableView dequeueReusableCellWithIdentifier:NSStringFromClass([leftTableCell class]) forIndexPath:indexPath];
        cell.curLeftTagModel = [self.dataList objectAtIndex:indexPath.section];
        cell.hasBeenSelected = (cell.curLeftTagModel==self.curSelectModel);
        
        //修改表格行默认分隔线存空隙的问题
        if ([cell respondsToSelector:@selector(setLayoutMargins:)]) {
            cell.layoutMargins=UIEdgeInsetsZero;
        }
        if ([cell respondsToSelector:@selector(setSeparatorInset:)]) {
            cell.separatorInset=UIEdgeInsetsZero;
        }
        
        return cell;
    }
    
    - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
        return tableCellHeight;
    }
    
    - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
        [tableView deselectRowAtIndexPath:indexPath animated:YES];
        
        _selectIndex=indexPath.section;
        self.curSelectModel=[self.dataList objectAtIndex:indexPath.section];
        [self.myTableView reloadData];
        
        [self predicateDataSoure];
        //处理点击在滚动置顶的问题
        [tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionTop animated:YES];
        
        self.isReturnLastOffset=NO;
    
        if (self.isKeepScrollState) {
            [self.myCollectionView scrollRectToVisible:CGRectMake(0, self.curSelectModel.offsetScorller, self.myCollectionView.frame.size.width, self.myCollectionView.frame.size.height) animated:NO];
        }
        else{
            
            [self.myCollectionView scrollRectToVisible:CGRectMake(0, 0, self.myCollectionView.frame.size.width, self.myCollectionView.frame.size.height) animated:NO];
        }
    }

    上面有两个知识点是关于表格默认分隔线距离左边的调整,把默认没有顶到左边的样式进行修改,另一个是关于点击时自动滚动到置顶的实现方式;

    3.3集合列表的UICollectionViewDataSource, UICollectionViewDelegate实现

    - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{
        return self.rightdataList.count;
    }
    
    - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
        rightCollectionViewCell *ccell = [collectionView dequeueReusableCellWithReuseIdentifier:NSStringFromClass([rightCollectionViewCell class]) forIndexPath:indexPath];
        noHeadRightModel *model=[self.rightdataList objectAtIndex:indexPath.row];
        ccell.curNoHeadRightModel=model;
        return ccell;
    }
    
    
    - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath{
        return [rightCollectionViewCell ccellSize];
    }
    - (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section{
        return UIEdgeInsetsZero;
    }
    - (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section{
        return 5;
    }
    - (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section{
        return 5;
    }
    
    - (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath{
        noHeadRightModel *model=[self.rightdataList objectAtIndex:indexPath.row];
        NSLog(@"你选择的%@",model.roomName);
    }

    3.4记录滚动的位置(右边列表的当前滚动位置记录下来,存在左边数据源的实体中,然后在左边的列表点击事件中进行判断)

    #pragma mark---记录滑动的坐标(把右边滚动的Y值记录在列表的一个属性中)
    
    -(void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
    {
        if ([scrollView isEqual:self.myCollectionView]) {
            self.isReturnLastOffset=YES;
        }
    }
    
    -(void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
    {
        if ([scrollView isEqual:self.myCollectionView]) {
            leftTagModel * item=self.dataList[self.selectIndex];
            item.offsetScorller=scrollView.contentOffset.y;
            self.isReturnLastOffset=NO;
        }
        
    }
    
    -(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView{
        if ([scrollView isEqual:self.myCollectionView]) {
            leftTagModel * item=self.dataList[self.selectIndex];
            item.offsetScorller=scrollView.contentOffset.y;
            self.isReturnLastOffset=NO;
            
        }
        
    }
    
    -(void)scrollViewDidScroll:(UIScrollView *)scrollView{
        if ([scrollView isEqual:self.myCollectionView] && self.isReturnLastOffset) {
            leftTagModel * item=self.dataList[self.selectIndex];
            item.offsetScorller=scrollView.contentOffset.y;
        }
    }

    源代码下载址

  • 相关阅读:
    内存泄漏检测
    qt 关于内存泄漏的检测
    Valgrind 安装与使用
    Qt应用中检测内存泄露——VLD
    Visual C++内存泄露检测—VLD工具使用说明
    ArcGIS Runtime支持的GP工具列表(转 )
    c# 调用ArcEngine的GP工具
    ArcEngine 数据导入经验(转载)
    在ArcEngine中使用Geoprocessing工具-执行工具
    利用C#与AE调用GP工具
  • 原文地址:https://www.cnblogs.com/wujy/p/4870133.html
Copyright © 2011-2022 走看看