代码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总结:
看到页面效果 慢慢分析,从大结构入手 , 每个部分 做了哪些事情 ,能公用的方法要抽取出来.那样方便我们思路分析.下期再见. ^ _ ^ 不要忘记给个赞
网友评论
-(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];
}
}
滚动标题滚动条,要加一个判断,不然小于也进行设置。