在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;@endstatic 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; }}
新闻热点
疑难解答