iOS 开发手把手教你封装一个高大上的多页面控制器,2018-0
前言:
小鹿哥最近又接到新的任务了,就是在分析需求的时候,我们要按照多个科目的班次对课程进行分类,那么多的课程怎么分类好呢;我们的主要目的就是让用户看了后能清晰明了的知道我们有哪些课程,这些课程分别是属于哪个科目的,然后结合自己的情况进行线上支付并购买。我们讨论了一下就决定对科目以及课程班次进行多页面控制,下面我就从技术的角度来实现这个功能。
1、两个collectionView,一个用作“表头”,一个用作“表身”。因为collectionView是复用collectionViewCell的自带内存回收机制,所以使用起来更加流畅,更加节省内存,所以小鹿哥比较推荐使用这种实现方式。
2、实现两个collectionView的布局:(titleCollectionView代表title容器,mainCollectionView代表控制器容器)
******首先是对titleCollectionView进行布局:******
-(UICollectionView *)titleCollectionView{
if (!_titleCollectionView) {
UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc]init];
layout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
layout.minimumInteritemSpacing = 0;
layout.minimumLineSpacing = 0;
layout.sectionInset = UIEdgeInsetsMake(0.0, 0.0, 0.0, 0.0);
layout.minimumLineSpacing = 5*SizeScale;//每行的间距)
_titleCollectionView = [[UICollectionView alloc]initWithFrame:CGRectMake(0, 64, WIDTH, 50)collectionViewLayout:layout];
if (ScreenWidth == 375 && ScreenHeight != 812) {
_titleCollectionView.frame = CGRectMake(0, 64, WIDTH, 44);
} else if (ScreenHeight == 812) {
_titleCollectionView.frame = CGRectMake(0, 84, WIDTH, 44);
} else if (ScreenWidth == 320) {
_titleCollectionView.frame = CGRectMake(0, 64, WIDTH, 44);
}
_titleCollectionView.backgroundColor=[UIColor whiteColor];
_titleCollectionView.delegate = self;
_titleCollectionView.dataSource = self;
_titleCollectionView.pagingEnabled = NO;
_titleCollectionView.bounces = YES;
_titleCollectionView.showsHorizontalScrollIndicator = NO;
_titleCollectionView.scrollsToTop = NO;
_titleCollectionView.scrollEnabled = YES;
[_titleCollectionView registerNib:[UINib nibWithNibName:@"SKHomeTitleCollectionViewCell" bundle:nil] forCellWithReuseIdentifier:cellId];
}
return _titleCollectionView;
}
******然后是对mainCollectionView进行布局:******
-(UICollectionView *)mainCollectionView{
if (!_mainCollectionView) {
UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc]init];
layout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
layout.minimumInteritemSpacing = 0;
layout.minimumLineSpacing = 0;
layout.sectionInset = UIEdgeInsetsMake(0.0, 0.0, 0.0, 0.0);
layout.minimumLineSpacing = 0;//每行的间距)
_mainCollectionView = [[UICollectionView alloc] initWithFrame:CGRectMake(0, 64+50, WIDTH, HEIGHT-114 - 49)collectionViewLayout:layout];
if (ScreenWidth == 375 && ScreenHeight != 812) {
_mainCollectionView.frame = CGRectMake(0, 64+44, WIDTH, HEIGHT-108 - 49);
} else if (ScreenHeight == 812) {
_mainCollectionView.frame = CGRectMake(0, 88+44, WIDTH, HEIGHT-132 - 49 - 34);
} else if (ScreenWidth == 320) {
_mainCollectionView.frame = CGRectMake(0, 64+44, WIDTH, HEIGHT-108 - 49);
}
layout.itemSize = _mainCollectionView.frame.size;
_mainCollectionView.backgroundColor = [UIColor whiteColor];
_mainCollectionView.delegate = self;
_mainCollectionView.dataSource = self;
_mainCollectionView.pagingEnabled = YES;
_mainCollectionView.bounces = NO;
_mainCollectionView.showsHorizontalScrollIndicator = NO;
_mainCollectionView.scrollsToTop = NO;
_mainCollectionView.scrollEnabled = YES;
[_mainCollectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"collectionMain"];
}
return _mainCollectionView;
}
******每个科目都带有一个下划线会随着标题的选择而移动我们把他添加在mainCollectionView上:******
[self.titleCollectionView addSubview:self.selectLineView];
- (UIView *)selectLineView {
if (!_selectLineView) {
_selectLineView = [[UIView alloc] init];
_selectLineView.backgroundColor = [UIColor colorWithHexString:@"007aff"];
}
return _selectLineView;
}
******其中有一个重要的变量来标记当前的titleCollectionView所选中的是哪一节课程我们定义为currentDex,当拖动mainCollectionView或者点击titleCollectionView的时候他的值都会随之变化,我们根据这个currentDex来确定当前选中的控制器,以及确定下划线selectLineView的布局******
- (void)layoutSubview {
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:currentDex inSection:0];
//设置选中偏移量
CGFloat itemCenterX = [self getCenterXWithRow:indexPath.row];
//设置底部红线
CGSize size = [self collectionView:self.titleCollectionView sizeForItemAtIndexPath:indexPath];
self.selectLineView.frame = CGRectMake(0, 0, size.width, 2);
self.selectLineView.center = CGPointMake(itemCenterX, self.titleCollectionView.frame.size.height - 8);
}
-(CGFloat)getCenterXWithRow:(NSInteger)row
{
if (row<0) {row=0;}
if (row>=[self.btnTitleArray count]) {row = [self.btnTitleArray count]-1;}
UICollectionViewLayoutAttributes * att = [self.titleCollectionView layoutAttributesForItemAtIndexPath:[NSIndexPath indexPathForRow:row inSection:0]];
return att.center.x;
}
- (CGSize)collectionView:(UICollectionView *)collectionView sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
if (collectionView == self.titleCollectionView) {
SKHomeBaseTitle *model=self.btnTitleArray[indexPath.row];
NSString * keyWords = model.titleStr;
CGSize size = [keyWords sizeWithAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:14 * SizeScale]}];
CGFloat itemWidth = size.width-10;
CGFloat itemHeight = self.titleCollectionView.frame.size.height;
return CGSizeMake(itemWidth, itemHeight);
}
if (collectionView==self.mainCollectionView) {
return collectionView.frame.size;
}
return CGSizeZero;
}
******确定科目的数量:******
NSArray *arr=@[@{@"name":@"法考(司考)",@"grouptypeid":@"1"},@{@"name":@"法律硕士",@"grouptypeid":@"4"},@{@"name":@"初级会计",@"grouptypeid":@"8"},@{@"name":@"中级会计",@"grouptypeid":@"9"},@{@"name":@"高级会计",@"grouptypeid":@"1"}];
for (int p=0; p<arr.count;p++){
NSDictionary *classDic = [arr objectAtIndex:p];
SKHomeBaseTitle *titleModel = [[SKHomeBaseTitle alloc]init];
if (p == 0) {
titleModel.orSelect=YES;
} else {
titleModel.orSelect=NO;
}
titleModel.titleStr=[classDic objectForKey:@"name"];
titleModel.grouptypeid = [classDic objectForKey:@"grouptypeid"];
[self.btnTitleArray addObject:titleModel];
}
[self.titleCollectionView reloadData];
其中的btnTitleArray就是存放的标题数据来源。
***初始化需要的视图控制器:***
- (void)getTitleData{
[self.vcArray removeAllObjects];
[self removeAllChildViewControllers ];
for (int i =0; I<self.btnTitleArray.count; I++){
if (i==0) {
S_FaKaoViewController *vc=[[S_FaKaoViewController alloc] init];
SKHomeBaseTitle *model = self.btnTitleArray[i];
[vc didMoveToParentViewController:self];
vc.labelNameStr=model.titleStr;
vc.grouptypeid=model.grouptypeid;
[self.vcArray addObject:vc];
[self addChildViewController:vc];
} else if (i==1) {
S_FaShuoViewController *vc=[[S_FaShuoViewController alloc] init];
SKHomeBaseTitle *model = self.btnTitleArray[i];
[vc didMoveToParentViewController:self];
vc.labelNameStr=model.titleStr;
vc.grouptypeid=model.grouptypeid;
[ self.vcArray addObject:vc];
[self addChildViewController:vc];
} else if (i==2) {
S_JuniorViewController *vc=[[S_JuniorViewController alloc] init];
SKHomeBaseTitle *model = self.btnTitleArray[i];
[vc didMoveToParentViewController:self];
vc.labelNameStr=model.titleStr;
vc.grouptypeid=model.grouptypeid;
[self.vcArray addObject:vc];
[self addChildViewController:vc];
} else if (i==3) {
S_MiddleViewController *vc=[[S_MiddleViewController alloc] init];
SKHomeBaseTitle *model = self.btnTitleArray[i];
[vc didMoveToParentViewController:self];
vc.labelNameStr=model.titleStr;
vc.grouptypeid=model.grouptypeid;
[ self.vcArray addObject:vc];
[self addChildViewController:vc];
} else {
SameAllViewController *sameVc = [[SameAllViewController alloc] init];
SKHomeBaseTitle *model = self.btnTitleArray[i];
[sameVc didMoveToParentViewController:self];
sameVc.labelNameStr=model.titleStr;
sameVc.grouptypeid=model.grouptypeid;
[ self.vcArray addObject:sameVc];
[self addChildViewController:sameVc];
}
}
[self.mainCollectionView reloadData];
[self.titleCollectionView reloadData];
}
#pragma mark --collectionViewDelegate
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
if (collectionView==self.titleCollectionView) {
SKHomeBaseTitle *model=self.btnTitleArray[indexPath.row];
if (ScreenWidth == 375) {
CGFloat w=[UILabel getwidthOfH:44.0f andText:model.titleStr andFontSize:14 * SizeScale];
return (CGSize){w+35*SizeScale,44};
} else {
CGFloat w=[UILabel getwidthOfH:50.0f andText:model.titleStr andFontSize:14 * SizeScale];
return (CGSize){w+35*SizeScale,50};
}
}else{
// 需要调试下
return (CGSize){WIDTH,HEIGHT-108};
}
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{
if (collectionView ==self.titleCollectionView) {
return _btnTitleArray.count;
}else{
return _btnTitleArray.count;
}
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
if (collectionView==self.titleCollectionView) {
SKHomeTitleCollectionViewCell *cell = [self.titleCollectionView dequeueReusableCellWithReuseIdentifier:cellId forIndexPath:indexPath];
SKHomeBaseTitle *model=self.btnTitleArray[indexPath.row];
cell.titleBtnLabel.font = [UIFont systemFontOfSize:14 * SizeScale];
cell.titleBtnLabel.text=model.titleStr;
if (model.orSelect==YES) {
cell.titleBtnLabel.textColor = [UIColor colorWithHexString:@"007aff"];
}else{
cell.titleBtnLabel.textColor = [UIColor colorWithHexString:@"333333"];
}
return cell;
}else{
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"collectionMain" forIndexPath:indexPath];
for (UIView *vc in cell.contentView.subviews) {
[vc removeFromSuperview];
}
UIViewController *viewC = (UIViewController *)self.vcArray[indexPath.item];
viewC.view.frame = cell.contentView.bounds;
[cell.contentView addSubview:viewC.view];
return cell;
}
}
// cell与cell之间的间隔,边距
- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section {
if (collectionView == self.titleCollectionView) {
return UIEdgeInsetsMake(0, 25, 0, 25);
} else {
//上左下右
return UIEdgeInsetsMake(0, 0, 0, 0);
}
}
这样就把两个collectionView的布局弄完了,剩下的就是处理滚动事件与点击头部事件所做的处理了。那么我们根据collectionView是集成ScrollocView的特性进行处理。
3、处理重要的页面之间交互事件:
******当页面滚动的时候******
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
if ([scrollView isEqual:self.mainCollectionView]) {
int p = self.mainCollectionView.contentOffset.x/WIDTH;
if (p==[self.btnTitleArray count]-1) {
p--;
}
SKHomeTitleCollectionViewCell *indexcell = (SKHomeTitleCollectionViewCell *)[self.titleCollectionView cellForItemAtIndexPath:[NSIndexPath indexPathForRow:p inSection:0]];
CGFloat indexCenterX = [self getCenterXWithRow:p];
NSInteger aimIndex = p+1;
SKHomeTitleCollectionViewCell *aimcell = (SKHomeTitleCollectionViewCell *)[self.titleCollectionView cellForItemAtIndexPath:[NSIndexPath indexPathForRow:aimIndex inSection:0]];
CGFloat aimIndexCenterX = [self getCenterXWithRow:aimIndex];
CGFloat scale = ABS(p*scrollView.width-scrollView.contentOffset.x)/scrollView.width;
self.selectLineView.centerX = indexCenterX+(aimIndexCenterX - indexCenterX)*scale;//设置位移
CGSize indexSize = [self collectionView:self.titleCollectionView sizeForItemAtIndexPath:[NSIndexPath indexPathForRow:p inSection:0]];
CGSize aimIndexSize = [self collectionView:self.titleCollectionView sizeForItemAtIndexPath:[NSIndexPath indexPathForRow:aimIndex inSection:0]];
self.selectLineView.width = (indexSize.width)+(aimIndexSize.width-indexSize.width)*scale;//设置宽度
if (scale>0.5) {
// [self collectionView:self.titleCollectionView didDeselectItemAtIndexPath:[NSIndexPath indexPathForRow:p inSection:0]];
aimcell.selected = YES;
}else{
// [self collectionView:self.titleCollectionView didDeselectItemAtIndexPath:[NSIndexPath indexPathForRow:aimIndex inSection:0]];
indexcell.selected = YES;
}
}
}
// 结束拖拽
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView{
if ([scrollView isEqual:self.mainCollectionView]) {
int p = self.mainCollectionView.contentOffset.x/WIDTH;
currentDex=p;
for (int i=0; i<self.btnTitleArray.count; I++){
SKHomeBaseTitle *mode= self.btnTitleArray[i];
if (i==p) {
mode.orSelect=YES;
}else{
mode.orSelect=NO;
}
}
[self.titleCollectionView reloadData];
NSIndexPath *nextIndexPath = [NSIndexPath indexPathForItem:p inSection:0];
[self.titleCollectionView scrollToItemAtIndexPath:nextIndexPath atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:YES];
[self layoutSubview];
}
}
******当点击每个标题的时候******
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath{
if (collectionView==self.titleCollectionView) {
[self changeMainCollectionViewContent:indexPath];
currentDex=indexPath.row;
for (int i=0; i<self.btnTitleArray.count;i++){
SKHomeBaseTitle *mode= self.btnTitleArray[i];
if (i==indexPath.row) {
mode.orSelect=YES;
}else{
mode.orSelect=NO;
}
}
[self.titleCollectionView reloadData];
[self layoutSubview];
} else {
}
}
最后就是在创建的每个视图控制器中根据往常的业务逻辑进行处理就行了,那么我们整个的封装就完成了,有不懂的小伙伴可以留言,或者加我的QQ号1849976000;我可以提供一下示例demo。
顺便把我所集效果给大家演示一下: