美文网首页iOS 控件定制iOS DeveloperiOS学习笔记
iOS篇-UI篇-仿今日头条效果父子控制器的应用

iOS篇-UI篇-仿今日头条效果父子控制器的应用

作者: TianTianBaby223 | 来源:发表于2017-06-22 11:33 被阅读626次


    代码demo 链接在评论里,喜欢的朋友点个赞.

    效果展示:

    TZ头条效果

    一 科普一分钟:

    1.控制器不能被销毁两种 情况 a: 全局 也就是说用strong 指针指向  b:添加成子控制器

    比如:我们在 A控制器 的View 添加 B控制器的View  B 中 添加 touchesBegan点击方法  现在我们用一若指针 weak  指向 B 或者几个局部变量B创建所得的View  结果就是 可以看到 B的View 在A .View 中显示 但是点击B 时没有反应 .因为B对象已经被销毁.

    2.ARC 管理原则:只要一个对象没有被强引用,就会被销毁.

    3.父子控制器运行原理:只要A控制器的View 成为B 控制器View的子视图 那么A控制器 就要成为B控制器的子控制器.

    二 代码实现和思路分析:

    实现GIF 效果 首先 我们要 建立两个部分,也就是上半部分展示标题的ScrollView 和下半部分展示内容的 ScrollView.

    @property(nonatomic,weak)UIScrollView*titleScrollView;

    @property(nonatomic,weak)UIScrollView*contentScrollView;

    static int const labelWith =100;

    添加子控制器 

    //添加所有子控制器

    -(void)setAllChildController{

    //推荐

    oneViewController*oneVC = [[oneViewControlleralloc]init];

    oneVC.title=@"推荐";

    [self addChildViewController:oneVC];

    //热点

    twoViewController*twoVC = [[twoViewControlleralloc]init];

    twoVC.title=@"热点";

    [self addChildViewController:twoVC];

    //沈阳

    threeViewController*threeVC = [[threeViewControlleralloc]init];

    threeVC.title=@"沈阳";

    [self addChildViewController:threeVC];

    //视频

    fourViewController*fourVC = [[four ViewControlleralloc]init];

    fourVC.title=@"视频";

    [self addChildViewController:fourVC];

    //社会

    fiveViewController*fiveVC = [[fiveViewControlleralloc]init];

    fiveVC.title=@"社会";

    [self addChildViewController:fiveVC];

    //图片

    sixViewController*sixVC = [[sixViewControlleralloc]init];

    sixVC.title=@"图片";

    [self addChildViewController:sixVC];

    }

    初始化标题ScrollView

    -(void)setupScrollView{

    NSInteger count =self.childViewControllers.count;

    //设置标题滚动条

    self.titleScrollView.contentSize=CGSizeMake(count *100,0);

    self.titleScrollView.showsHorizontalScrollIndicator=NO;

    //设置内容滚动条

    self.contentScrollView.contentSize=CGSizeMake(WIDTH*count,0);

    //开启分页

    self.contentScrollView.pagingEnabled=YES;

    //没有弹簧效果

    self.contentScrollView.bounces=NO;

    //隐藏水平滚动条

    self.contentScrollView.showsHorizontalScrollIndicator=NO;

    //设置协议

    self.contentScrollView.delegate=self;

    }

    添加所有子控制器对应标题以及标题设置

    -(void)setupTitleLabel{

    NSInteger count =self.childViewControllers.count;

    CGFloat x  =0;

    CGFloaty =0;

    CGFloat height =44;

    for(int i =0; i < count; i++) {

    //取出控制器

    UIViewController*vc =self.childViewControllers[i];

    //创建label

    UILabel*label = [[UILabel alloc]init];

    //添加label到titleArray数组

    [self.titleArray addObject : label];

    label.font= [UIFont systemFontOfSize:16];

    label.textAlignment=NSTextAlignmentCenter;

    x = i*labelWith;

    label.tag= i;

    //设置尺寸

    label.frame=CGRectMake(x, y,labelWith, height);

    //设置Label文字

    label.text= vc.title;

    //添加手势

    UITapGestureRecognizer*tap = [[UITapGestureRecognizer alloc] initWithTarget : self action:@selector(titleClick:)];

    label.userInteractionEnabled=YES;

    [label addGestureRecognizer : tap];

    //设置高亮文字颜色

    label.highlightedTextColor= [UIColor redColor];

    //默认选中第0个label

    if(i ==0) {

    [self titleClick : tap];

    }

    //添加label

    [self.titleScrollView addSubview : label];

    }

    }

    UI 部分我们搭建完成了,接下来我们来处理 点击标题的业务逻辑 ,我们先分析一下 点击标题label 应该做哪些事情

    1.让标题选中,文字为空色

    2.滚动到对应的位置

    3.对应的位置添加子控制器View

    4.让选中标题居中

    -(void)titleClick:(UITapGestureRecognizer*)tap{

    //0取出label

    UILabel*label = (UILabel*)tap.view;

    //1.标题颜色高亮

    [self selectLabel : label];

    //2.滚动相应位置

    NSInteger index = label.tag;

    //2.1计算滚动位置

    CGFloat offsetX = label.tag*WIDTH;

    [self.contentScrollView setContentOffset:CGPointMake(offsetX,0)animated:YES];

    //3.对应位置添加控制器View

    [self showVC : index];

    //4.让选中标题居中

    [self setUpTitleCenter : label];

    }

    接下来各个击破里面的方法

    首先是 选择 三部曲 创建 临时变量

    a:@property(nonatomic,weak)UILabel*seletLabel;

    b:做事情

    c:参数 label 赋值给 临时变量

    -(void)selectLabel:(UILabel*)label{

    _seletLabel.highlighted = NO;

    //取消形变

    _seletLabel.transform=CGAffineTransformIdentity;

    _seletLabel.textColor= [UIColor blackColor];

    label.highlighted=YES;

    //形变

    label.transform=CGAffineTransformMakeScale(1.3,1.3);

    _seletLabel = label;

    }

    对应位置添加控制器View 注意的是 优化考虑 加判断 是否被加载过,加载过的视图一律return

    -(void)showVC:(NSInteger)index{

    CGFloat offsetX = index *WIDTH;

    UIViewController*vc =self.childViewControllers[index];

    //判断当前控制器的View有没有加载过如果已经加载过就不需要加载

    if(vc.isViewLoaded)return;

    vc.view.frame=CGRectMake(offsetX,0,WIDTH,self.contentScrollView.frame.size.height);

    [self.contentScrollView addSubview: vc.view];

    }

    让选中标题居中 思路分析

    a:滚动的范围就是  标题的中心点 减去 屏幕中心点的距离 就是 移动后的偏移量

    b:假如 得出的结果小于0 我们可以分析出 label的其实点在屏幕中心点的左边 也就是 说可以不做事情

    c:当滚动到最大的滚动范围之后 在滚动 我们要把滚动范围就设置为最大滚动范围

    -(void)setUpTitleCenter:(UILabel*)centerLabel{

    //计算偏移量

    CGFloat offsetX = centerLabel.center.x-WIDTH*0.5;

    if(offsetX <0) {

    offsetX =0;

    }

    //最大滚动范围

    CGFloat maxOffsetX =self.titleScrollView.contentSize.width-WIDTH;

    if(offsetX > maxOffsetX) {

    offsetX = maxOffsetX;

    }

    //滚动标题滚动条

    [self.titleScrollView setContentOffset:CGPointMake(offsetX,0)animated:YES];

    }

    分析到这里 上半部分的scroll 业务逻辑已经处理完了 ,接下来我们分析 内容的scroll该如何做处理

    首先我们写代理方法:滑动结束时,从今日头条 可以分析出,都是滑动停止的时候页面才开始做事情,所以我们要用scrollViewDidEndDecelerating 这个代理方法

    思路分析

    a:首先和点击标题一样 添加 子控制器View

    b:把对应标题选中

    c:标题居中

    具体方法和上述一样

    -(void)scrollViewDidEndDecelerating:(UIScrollView*)scrollView{

    //计算滚动到哪一页

    NSInteger index = scrollView.contentOffset.x/scrollView.bounds.size.width;

    //1.添加子控制器View

    [self showVC :index];

    //2.把对应标题选中

    UILabel*label =self.titleArray[index];

    [self selectLabel : label];

    //3让选中标题居中

    [self setUpTitleCenter :label];

    }

    最后计算缩放比例 和 字体的渐变

    思路分析 :可以看的出来 字体的变化 和颜色的变化 是渐渐产生的,我们会迅速想到的就是scrollView的代理方法 ,而且 无论做任何事 内容scrollView 都是做事情的 所以接下来我们就要在scrollViewDidScroll 这个代理方法里处理

    //滚动就会调用

    算法分析

    a:要想对label 文章首先 我们必须拿到操作的label 通过scrollview的偏移量 / WIDTH 取整数 我们可以知道当前处理的 label  也就是左边的Label 右边label 就是下角标+1

    b:字体变化  可以分析出来 右边的label 相对于 左边的 label 的大小 在0-1 之间 增加或者减小

    所以  CGFloat currpage = scrollView.contentOffset.x/WIDTH; 得到的currpage 再减去 它的取整 int currpage 就算了 右边 label 对于 左边label 的相对变化  scale 而左边label 相对于右边label的相对变化 就是相反的 1-scale.

    -(void)scrollViewDidScroll:(UIScrollView*)scrollView{

    CGFloat currpage = scrollView.contentOffset.x/WIDTH;

    //左边角标

    NSInteger leftIndex = currpage;

    //右边角标

    NSInteger rightIndex = leftIndex +1;

    //获取左边label

    UILabel*leftLabel =self.titleArray[leftIndex];

    //获取右边label

    UILabel*rightLabel;

    if(rightIndex <=self.titleArray.count-1) {

    rightLabel =self.titleArray[rightIndex];

    }

    //计算右边缩放比例右边自己相对于左边字体变大比例

    CGFloat rightscal = currpage - leftIndex;

    //计算左边缩放比例

    CGFloat leftscal =1- rightscal;

    //缩放比例1~1.3

    //左边缩放比例

    leftLabel.transform=CGAffineTransformMakeScale(leftscal*0.3+1,leftscal*0.3+1);

    //右边缩放比例

    rightLabel.transform=CGAffineTransformMakeScale(rightscal*0.3+1,rightscal*0.3+1);

    //设置文字颜色渐变

    /**RGB渐变*/

    leftLabel.textColor= [UIColor colorWithRed :leftscalgreen :0blue:0alpha:1];

    rightLabel.textColor= [UIColor colorWithRed :rightscalgreen :0blue:0alpha:1];

    }

    到这里就是实现了 所有核心 功能 . 相信你可以做的出来一样的效果了.

    3总结:

    看到页面效果 慢慢分析,从大结构入手 , 每个部分 做了哪些事情 ,能公用的方法要抽取出来.那样方便我们思路分析.下期再见.  ^ _ ^ 不要忘记给个赞

    相关文章

      网友评论

      • SandyLai_cbba:麻烦楼主发我一份代码 :864461894@qq.com
        TianTianBaby223:@SandyLai_cbba 好,周一的
      • zl520k://设置标题居中
        -(void)setUpTitleCenter:(UILabel*)centerLabel{
        //计算偏移量
        CGFloat offsetX = centerLabel.center.x - WIDTH*0.5;

        if (offsetX < 0) {
        offsetX = 0;
        }

        //最大滚动范围
        CGFloat maxOffsetX = self.titleScrollView.contentSize.width - WIDTH;
        if (offsetX > maxOffsetX) {
        offsetX = maxOffsetX;

        }

        //滚动标题滚动条
        if (offsetX >= 0)
        {
        [self.titleScrollView setContentOffset:CGPointMake(offsetX, 0) animated:YES];
        }

        }
        滚动标题滚动条,要加一个判断,不然小于也进行设置。
      • zl520k:demo在你公布的网址,无法下载,有没有github的网址?
        TianTianBaby223:@zl520k 发给你了,请注意查收
        zl520k:@TianTianBaby223 我的邮箱:zl520k@163.com,谢谢
        TianTianBaby223:@zl520k 暂时没有,邮箱给我,我发给你
      • Lonely__M:Markdown 排版下
        TianTianBaby223:嗯,下期注意:smile:
      • 时间不会倒着走:这样的话,在进入页面后是同时加载多个页面么?
        TianTianBaby223:@时间不会倒着走 有一个判断 根据 if(vc.isViewLoaded)return;判断,假如已经加载 直接返回。你可以去下载代码
        时间不会倒着走:@TianTianBaby223 代码中哪里有体现啊,我也写了一个就同时会加载多个界面
        TianTianBaby223: @时间不会倒着走 不会的,当点击标题或者滑动的时候才添加子控制器视图。

      本文标题:iOS篇-UI篇-仿今日头条效果父子控制器的应用

      本文链接:https://www.haomeiwen.com/subject/xomxcxtx.html