精度解析百思不得姐流行框架之精华版

0 框架效果图

一 讲解顺序

1 标题部分

2 内容显示部分

3 完善代码

4 知识补充

二 内容显示部分解析

1 搭建: 通过观察该部分运行情况,支持上下滑动,同时也支持左右滑动

—-> 1.1 结论: 父控件采用UIScrollView;子控件采用五个tableView

2 分析一: 考虑内容显示的数据量比较大

—-> 2.1 做法: tableView采用循环利用

3 tableView的排列顺序: 设置scrollerView的偏移量为5个tableView的宽度(contentSize),将tableView依次从屏幕向右排列

4 观察:父控件scrollerView不能上下滑动,设置上下滑动的contentSize为0

5 各部分的分工情况: scrollerView:负责左右滚动; tableView:负责上下滚动

6 scrollerView的frame决定可视范围;scrollerView的contentSize决定滑动范围

三 标题栏显示部分解析

1 观察:标题栏并不能滑动

—-> 1.1 结论:可以通过UIView来实现

2 点击的标题可选方案: 1> button 2> Label

3 最终方案:选择按钮

4 选中后显示的下划线:采取用一个UIView来实现

四 标题栏相关说明和代码

1 通过观察,可以通过标题栏看到底部的内容,说明标题栏是有透明度的,需要特别设置

2 设置标题的透明度(四种方法:三种可取,一种不可取)

—-> 2.1 方法一 :—->可取
titleView.backgroundColor = [[UIColor whiteColor] colorWithAlphaComponent:0.5];
—-> 2.2 方法二:—->可取
titleView.backgroundColor = [UIColor colorWithWhite:1.0 alpha:0.5];
—-> 2.3 方法三: —->可取
titleView.backgroundColor = [UIColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:0.5];
—-> 2.4 方法四:—->不可取
—-> 不可取原因:内部的子控件也会变透明
titleView.alpha = 0.3;

3 添加标题栏的代码

#pragma mark - 添加标题栏
- (void)setUpTitlesView
{
    //创建UIView对象
    UIView *titleView = [[UIView alloc] init];
    //设置背景色的透明度(方法一)-->可取
    titleView.backgroundColor = [[UIColor whiteColor] colorWithAlphaComponent:0.5];
    //设置尺寸
    titleView.frame = CGRectMake(0, XFJ_navBar, XFJ_screenW, 35);
    //添加到view中
    [self.view addSubview:titleView];
    //赋值
    self.titleView = titleView;
    //调用添加按钮的方法
    [self setUptitlesButton];

    //按钮的标题下划线
    [self setUpTitlesUnderLine];
}

五 添加标题栏中的按钮

1 采取for循环的方式创建

2 内部写入对按钮的监听方法

3 创建的按钮为自定义按钮

4 有关点击后按钮的状态设置

—> 添加按钮的代码:
#pragma mark - 添加标题中的按钮
- (void)setUptitlesButton
{
    //包装数组
    NSArray *titles = @[@"全部",@"视频",@"声音",@"图片",@"段子"];
    //取出数组里面的标题总数
    NSInteger count = titles.count;
    CGFloat titleButtonX = 0;
    CGFloat titleButtonY = 0;
    //设置按钮的宽度
    CGFloat titleButtonW = self.titleView.XFJ_Width / count;
    //按钮的高度
    CGFloat titleButtonH = self.titleView.XFJ_Height;
    for (NSInteger i = 0; i < count; i++) {
        //创建按钮
        XFJTitleButton *titleButtons = [[XFJTitleButton alloc] init];
        titleButtonX = i * titleButtonW;
        //给按钮绑定tag
        titleButtons.tag = i;
        //设置尺寸
        titleButtons.frame = CGRectMake(titleButtonX, titleButtonY, titleButtonW, titleButtonH);
        //给按钮添加标题
        [titleButtons setTitle:titles[i] forState:UIControlStateNormal];
        //监听按钮的点击
        [titleButtons addTarget:self action:@selector(titleButtonClick:) forControlEvents:UIControlEventTouchUpInside];
        //添加按钮
        [self.titleView addSubview:titleButtons];
    }
}

六 按钮监听

1 按钮颜色的变化(三种方法:一种通过改变颜色;二种通过改变按钮的状态)

—> 1.1 直接通过改变颜色(传统的做法)
#pragma mark - 实现监听中的方法
- (void)titleButtonClick:(UIButton *)button
{
    //让上一个按钮的状态为黑色
    [self.previousButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
    //让当前的按钮为红色
    [button setTitleColor:[UIColor redColor] forState:UIControlStateNormal];
    //将当前按钮的颜色状态给上一个按钮
    self.previousButton = button;
—> 1.2 传统方法的局限性:不方便修改文字的颜色
—> 1.3 方法一如果需要修改,就取决于设置的颜色
[titleButtons setTitleColor:[UIColor darkGrayColor] forState:UIControlStateNormal];
[titleButtons setTitleColor:[UIColor redColor] forState:UIControlStateSelected];

2 直接通过改变按钮的状态来改变按钮的颜色

—> 2.1 局限性:按钮的颜色能改变,但是再次点击按钮的时候,按钮不能接受事件.
—> 2.2 按钮设置的状态代码
[titleButtons setTitleColor:[UIColor darkGrayColor] forState:UIControlStateNormal];
[titleButtons setTitleColor:[UIColor redColor] forState:UIControlStateDisabled];

2.3 按钮实现代码:(依照公司需求而定)

//让上一个按钮的状态为灰色
    self.previousButton.enabled = YES;
    //让当前按钮为红色
    button.enabled = NO;
    //将当前按钮的颜色状态给上一个按钮
    self.previousButton = button;

七 按钮下划线的相关设置

1 下划线随着按钮的切换而变化

2 该功能抽成一个方法,调用位置—>创建标题栏方法中调用

—> 代码:
#pragma mark - 添加标题按钮的选中线
- (void)setUpTitlesUnderLine
{
    //取出按钮
    XFJTitleButton *firstTitleButton = self.titleView.subviews.firstObject;
    //创建下划线的view
    UIView *underViewLine = [[UIView alloc] init];
    CGFloat underViewLineX = 0;
    CGFloat underViewLineH = 1;
    CGFloat underViewLineY = self.titleView.XFJ_Height - underViewLine.XFJ_Height;
    CGFloat underViewLineW = 0;
    //尺寸
    underViewLine.frame = CGRectMake(underViewLineX, underViewLineY, underViewLineW, underViewLineH);
    //下划线的颜色状态和第一个按钮一样
    underViewLine.backgroundColor = [firstTitleButton titleColorForState:UIControlStateSelected];
    //将按钮加入到标题中
    [self.titleView addSubview:underViewLine];
    //设置第一个按钮的状态
    firstTitleButton.selected = YES;
    //让第一个按钮的状态成为上一个按钮状态
    self.previousButton = firstTitleButton;
    //设置下划线的大小自适应
    [firstTitleButton.titleLabel sizeToFit];
    //赋值
    self.underViewLine = underViewLine;
    //设置线的宽度
    self.underViewLine.XFJ_Width = firstTitleButton.titleLabel.XFJ_Width + 10;
    //设置线的中心点和按钮对齐
    self.underViewLine.XFJ_centerX = firstTitleButton.XFJ_centerX;
}

3 注意点一:必须要先设置先的宽度,才能设置线的中心点对齐方式,否则会出现启动app的时候按钮的下划线会有一段动画效果.

4 注意点二:让第一个按钮成为选中状态千万不能省,省了的话也会有莫名其妙的错误

5 点击按钮,下划线移动到对应的按钮下,代码书写位置:实现对按钮点击的监听方法中

//点击按钮移动下划线
    [UIView animateWithDuration:0.25 animations:^{

        //设置线的宽度
        self.underViewLine.XFJ_Width = button.titleLabel.XFJ_Width + 10;
        //设置线的中心点和按钮对齐
        self.underViewLine.XFJ_centerX = button.XFJ_centerX;

八 内容scrollerView相关设置

1 重点:关闭自动调节内边距

—> 1.1 原因:当有子控件加入到UIScrollView中时,子控件顶部的间距会默认往下移动64的间距(只针对UIScrollView中的子控件)

2 关闭UIScrollView的点击顶部状态栏,tableView内容回到顶部(为后面状态栏设置埋伏笔)

3 设置contenSize,否则tableView将无法滚动

4 优化代码部分(后面补上)

5 总体代码部分:

#pragma mark - 添加内容的scrollerView
- (void)setUpAllChindView
{
    //不要调整scrollerView的内边距
    self.automaticallyAdjustsScrollViewInsets = NO;
    //创建scrollerView
    UIScrollView *contentView = [[UIScrollView alloc] init];
    contentView.frame = self.view.bounds;
    //这个scrollerView不需要回到顶部
    contentView.scrollsToTop = NO;
    //添加到内容的view中
    [self.view addSubview:contentView];

    //添加子控制器的view到scrollerView中
    //取出控制器的数量
    NSInteger count = self.childViewControllers.count;
    for (NSInteger i = 0; i < count; i++) {
        //根据对应的i值取出控制器的view(该行代码是照成一次性创建全部tableview的原因)
        UIView *childView = self.childViewControllers[i].view;
        //设置尺寸
        childView.frame = CGRectMake(i * XFJ_screenW, 0, XFJ_screenW, XFJ_screenH);

        //添加view到scrollerView中
        [contentView addSubview:childView];
    }
    //开启分页
    contentView.pagingEnabled = YES;
    //设置代理
    contentView.delegate = self;
    //设置滚动范围
    contentView.contentSize = CGSizeMake(count * XFJ_screenW, 0);
    //隐藏滚动条
    contentView.showsHorizontalScrollIndicator = NO;
    contentView.showsVerticalScrollIndicator = NO;
    //赋值
    self.contentView = contentView;
}

九 标题与对应的tableView联动(一)

1 联动:开发中专业术语.意思是:通过改变一方某个状态,另外一方也会跟着改变

2 点击标题栏中的按钮,切换对应的控制器的view

3 代码书写位置

–> 1 由于是点击按钮切换对应的控制器,那么代码书写的位置肯定是在按钮监听实现部分,并且移动的时候也需要动画

—-> 2 联动三种方法:

—-> 2.1 通过在创建按钮的时候给按钮绑定tag(方法一)—>(第五部分tag已经添加)
//标题和子控制器view的联动问题(第一种方法)
        self.contentView.contentOffset = CGPointMake(button.tag * XFJ_screenW, self.contentView.bounds.origin.y);
            2.2 通过绑定的tag修改UIScrollView的偏移量(方法二)
//标题和子控制器view的联动问题(第二种方法)-->直接修改偏移量
        CGPoint offset = self.contentView.contentOffset;
        offset.x = button.tag * XFJ_screenW;
        self.contentView.contentOffset = offset;
       2.3 通过遍历标题栏的子控件(方法三:相对于上面2种比较好性能,但是如果按钮比较少,这方面问题也是不存在的)
//通过遍历的方法(方法三:相对于上面比较耗性能)--->遍历
        NSInteger index = [self.titleView.subviews indexOfObject:button];
        self.contentView.contentOffset = CGPointMake(index * XFJ_screenW,
                                                     self.contentView.bounds.origin.y);

十 tableView与对应的标题联动(二)

1 实现方案:直接通过代理就可以实现

2 思路: 通过偏移量和屏幕宽度的计算得到tableView的索引

3 添加内容UIScrollView的时候设置代理(第八部分实现)

4 代码:(代理方法:滑动完毕的时候调用)

#pragma mark - 滑动完毕的时候调用
#pragma mark - scrollerView的代理方法(设置拖动scrollerView与标题按钮联动)
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
    //计算拖动索引
    NSInteger index = scrollView.contentOffset.x / XFJ_screenW;
    //根据索引取出对应的按钮
    XFJTitleButton *titleButton = self.titleView.subviews[index];
    //方法
    [self titleButtonClick:titleButton];
}

十一 点击状态栏回到顶部

1 该功能苹果内部已经帮我们实现了(方法一)

2 使用这种架构搭建的时候,点击状态栏,回到顶部的功能失效了

3 失效原因:点击状态栏回到顶部,这种功能只针对UIScrollView才有效果,但是我们至少往控制器中添加了六个UIScrollView,所有状态栏无法识别.

4 做法:禁止父控件ScrollView接收回到顶部的方法

5 判断是否加载了view;判断view的类型是否是UIScrollView的类型

—> 6 代码:
/* 设置点击顶部状态栏,内容回到顶部(只有UIScrollerView才有这功能) */
    //遍历
    for (NSInteger i = 0; i < self.childViewControllers.count; i++) {
        //取出所有的子控制器
        UIViewController *childVC = self.childViewControllers[i];
        //判断如果没有加载了view就跳过
        if (![childVC isViewLoaded]) continue;
        //如果不是这种类型就跳过
        if (![childVC.view isKindOfClass:[UIScrollView class]]) continue;
        //如果类型是UIScrollView的类型
        UIScrollView *scrollView = (UIScrollView *)childVC.view;
        scrollView.scrollsToTop = (i == index);
    }

7 方法二:(不推荐使用)

—> 7.1 在创建一个窗口,只有在创建一个窗口,才不会遮住状态栏,但是这样做会引起一系列的问题,所以不推荐使用,但是这种功能还是需要的.比如,很多电商应用都在界面中设置了一个可以移动并且点击的购物车,这就是利用新创建的窗口实现的.所以这种方法慎用.

十二 全屏穿透

1 设置内边距实现全屏穿透的效果,让tableView所有的内容都能在内容区域显示,并且顶部内容和底部内容不会被tabBar和导航条遮住

2 实现代码:(必须在每一个tableView控制器中都依次添加)

//设置view的颜色
    self.tableView.backgroundColor = XFJ_randomColor;
    //设置内边距(内容的额外边距)
    self.tableView.contentInset = UIEdgeInsetsMake(99, 0, 49, 0);
    //设置tableView的滚动条
    self.tableView.scrollIndicatorInsets = self.tableView.contentInset;

十三 添加所有的子控制器(这部分没什么好介绍的,看懂就行)

#pragma mark - 添加所有的tableView的view到内容的scrollerView
- (void)setUpAllTableView
{
    //添加子控制器
    [self addChildViewController:[[XFJAllViewController alloc] init]];
    [self addChildViewController:[[XFJPassageViewController alloc] init]];
    [self addChildViewController:[[XFJPictureViewController alloc] init]];
    [self addChildViewController:[[XFJVideoViewController alloc] init]];
    [self addChildViewController:[[XFJVoiceViewController alloc] init]];
}

十四 优化

1 tableView的懒加载

2 自定义按钮的封装

3 经过测试,如果按照以上的代码书写,能达到效果,但是我发现这样写的问题是tableView无法实现懒加载.原因是因为在第(八点),取出父控件中子控件的view的时候,在viewDidLoad中打印的结果显示,一开始就会创建5个tableView,所以不存在懒加载.

4 改进方案:在点击按钮完成的时候,我们在创建并且添加按钮.

—-> 4.1 代码块一:
......}completion:^(BOOL finished) {

        //添加对应的index到scrollerView中
        [self addChildVCIntoScrollView:index];
    }];
    4.2 方法:(内部做出了判断,如果父控件中存在view的时候,我们就不在创建)
#pragma mark - 添加对应的子控制器的view到scrollerView中
- (void)addChildVCIntoScrollView:(NSInteger)index
{
    //根据对应的index取出对应的子控件器
    UIViewController *childVC = self.childViewControllers[index];
    //判断,如果子控制器已经加载过了,那么直接返回,不再添加了
    if ([childVC isViewLoaded]) return;
    //该句就是上面那句的简写
    childVC.view.frame = self.contentView.bounds;
    //将view加入到scrollerView中
    [self.contentView addSubview:childVC.view];
}

十五 知识点补充

1 frame.size.height:矩形框的高度

2 什么是tableView的内容(content)?

—> 2.1 cell
—> 2.2 tableHeaderView\tableFooterView
—> 2.3 sectionHeader\sectionFooter

3 contentSize.height:内容的高度

4 contentOffset.y:内容的偏移量(frame的顶部-contentSize的顶部)

5 contentInset:内容周围的间距(内边距) frame:以父控件内容的左上角为坐标原点

十六 疑问

1 问题: 需要当用户运行app的时候,默认第0个按钮为选中状态,并且按钮的下划线也默认在按钮的下面 ,但是运行的时候,下滑线没有显示出来

—> 原因:在viewDidLoad中加载的时候顺序出了问题

2 疑问:我们依次给每个tableView都加一个顶部内边距和底部内边距,是不是太累了,为什么不直接给tableView的父控件scrollerView直接加一个顶部内边距和底部内边距?

—> 解答: scrollerView的子控件tableView会整个往下移动64的距离,同时全屏穿透效果消失.—>内边距影响

3 很多人拖动tableView滑动的时候,看到了tableView之间有一根绿色的线条,这不是bug,是模拟器的原因.

十七 总结

1 该部分讲解完全解析了当下流行框架和仿百思的框架的搭建步骤,里面都解析到了所有发生的问题,希望能帮到大家.

2 最后大家如果觉得我的博客能满足大家的需求,希望能及时的给我留言,有什么问题的话,希望也及时给我留言.如果大家觉得我的博客还可以的话,那么希望您关注我的官方博客,谢谢!!!!

你可能感兴趣的:(框架,数据,控件,uiscrollview)