重点:ContentOffset偏移量计算
判断防止View视图重复加载
UIViewController * vc = self.childViewControllers[i]; //FIXME:防止重复加载View视图 if (vc.viewIfLoaded) { //iOS 9.0之后 return; } //FIXME:通用 if (vc.view.superview) { return; }
代码:(未抽取)
#import "ViewController.h" #import "TopLineViewController.h" #import "HotViewController.h" #import "VideoViewController.h" #import "ScienceViewController.h" #import "SocietyViewController.h" #import "ReaderViewController.h" #define ScreenW [[UIScreen mainScreen] bounds].size.width #define ScreenH [[UIScreen mainScreen] bounds].size.height @interface ViewController ()<UIScrollViewDelegate> /** 标题按钮数组 */ @property (nonatomic, strong) NSMutableArray * titleBtns ; /** 上一次选中的按钮 */ @property (nonatomic, strong) UIButton * selectedBtn ; @property (nonatomic, weak) UIScrollView *titleScrollView; @property (nonatomic, weak) UIScrollView *contentScrollView; @end @implementation ViewController -(NSMutableArray *)titleBtns { if (!_titleBtns) { _titleBtns = @[].mutableCopy; } return _titleBtns; } - (void)viewDidLoad { [super viewDidLoad]; self.navigationItem.title = @"网易新闻"; // 1.添加标题滚动视图 [self setupTitleScrollView]; // 2.添加内容滚动视图 [self setupContentScrollView]; //3.添加所有的子控制器 [self setupAllChildViewController]; //4.设置所有的标题 [self setupAllTitles]; //5.处理标题的点击 //7.FIXME:scrollView的额外滚动区域64 //iOS 7以后,导航控制器中scrollView顶部会添加64的额外滚动区域 self.automaticallyAdjustsScrollViewInsets = NO; } #pragma mark - 设置所有的标题 - (void)setupAllTitles { //已经把我们想要的内容展示上去 -> 展示的效果是否是我们想要的(调整细节) //1.标题的颜色 为黑色 //2.需要让titleScrollView可以滚动 //添加所有标题按钮 NSInteger count = self.childViewControllers.count; CGFloat btnW = 100; CGFloat btnH = self.titleScrollView.bounds.size.height; CGFloat btnX = 0; for (NSInteger i = 0; i<count; i++) { UIButton *titleBtn = [UIButton buttonWithType:UIButtonTypeCustom]; titleBtn.tag = i; //设置标题 UIViewController * vc = self.childViewControllers[i]; [titleBtn setTitle:vc.title forState:UIControlStateNormal]; //设置Frame btnX = i * btnW; titleBtn.frame = CGRectMake(btnX, 0, btnW, btnH); [titleBtn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; //监听按钮点击 [titleBtn addTarget:self action:@selector(titleClick:) forControlEvents:UIControlEventTouchUpInside]; //设置第一个按钮默认选中(头条默认选中) if (i == 0) { [self titleClick:titleBtn]; } //把标题按钮保存到对应的数组中 [self.titleBtns addObject:titleBtn]; [self.titleScrollView addSubview:titleBtn]; } //设置标题滚动范围 self.titleScrollView.contentSize = CGSizeMake(count * btnW, 0); self.titleScrollView.showsHorizontalScrollIndicator = NO; //6.设置内容的滚动范围 self.contentScrollView.contentSize = CGSizeMake(count * ScreenW, 0); //bug:代码跟我的一样,但是标题显示不出来 //bug: 内容往下移动,莫名其妙 //FIXME:9.选中标题居中处理->选中标题 //10.标题文字缩放 } #pragma mark - 选中标题 - (void)selButton:(UIButton*)button { _selectedBtn.transform = CGAffineTransformIdentity; [_selectedBtn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; [button setTitleColor:[UIColor redColor] forState:UIControlStateNormal]; //_selectedBtn = button; //标题居中处理 [self setupTitleCenter:button]; //字体缩放: 形变 button.transform = CGAffineTransformMakeScale(1.3, 1.3); _selectedBtn = button; [self setupTitleScale]; } #pragma mark - 字体缩放 - (void)setupTitleScale { } #pragma mark - 标题居中处理 - (void)setupTitleCenter:(UIButton*)button { //本质:修改titleScrollView的偏移量 CGFloat offsetX = button.center.x - ScreenW * 0.5; NSLog(@"offsetX==%f",offsetX); //头条、热点(offsetX<0) if (offsetX < 0) { offsetX = 0; } //最大偏移量计算(解决订阅和科技) CGFloat maxOffsetX = self.titleScrollView.contentSize.width - ScreenW; if (offsetX > maxOffsetX) { offsetX = maxOffsetX; } [self.titleScrollView setContentOffset: CGPointMake(offsetX, 0) animated:YES]; //此时有bug:点击每个按钮都居中了,例如:头条、热点(offsetX<0) } #pragma mark - 添加一个子控制器的View - (void)setupOneViewController:(NSInteger)i { UIViewController * vc = self.childViewControllers[i]; //FIXME:防止重复加载View视图 // if (vc.viewIfLoaded) { // //iOS 9.0之后 // return; // } //FIXME:通用 if (vc.view.superview) { return; } CGFloat x = i * ScreenW; vc.view.frame = CGRectMake(x, 0, ScreenW, self.contentScrollView.bounds.size.height); [self.contentScrollView addSubview:vc.view]; } #pragma mark - 处理标题点击 - (void)titleClick:(UIButton*)button { //FIXME:8.监听内容视图滚动 NSInteger i = button.tag; //1.标题颜色变为红色 [self selButton:button]; //2.把对应子控制器的View添加上去 [self setupOneViewController:i]; //3.滚动到对应的位置 self.contentScrollView.contentOffset = CGPointMake(i * ScreenW, 0); } #pragma mark - 添加所有子控制器 - (void)setupAllChildViewController { //头条 TopLineViewController * vc = [TopLineViewController new]; vc.title = @"头条"; [self addChildViewController:vc]; //热点 HotViewController * hotVC = [HotViewController new]; hotVC.title = @"热点"; [self addChildViewController:hotVC]; //视频 VideoViewController * videoVC = [VideoViewController new]; videoVC.title = @"视频"; [self addChildViewController:videoVC]; //社会 SocietyViewController * societyVC = [SocietyViewController new]; societyVC.title = @"社会"; [self addChildViewController:societyVC]; //订阅 ReaderViewController * readerVC = [ReaderViewController new]; readerVC.title = @"订阅"; [self addChildViewController:readerVC]; //科技 ScienceViewController * scienceVC = [ScienceViewController new]; scienceVC.title = @"科技"; [self addChildViewController:scienceVC]; } #pragma mark - 添加标题滚动视图 - (void)setupTitleScrollView { // 创建titleScrollView UIScrollView *titleScrollView = [[UIScrollView alloc] init]; //titleScrollView.backgroundColor = [UIColor redColor]; CGFloat y = self.navigationController.navigationBarHidden ? 20 : 64; titleScrollView.frame = CGRectMake(0, y, self.view.bounds.size.width, 44); [self.view addSubview:titleScrollView]; _titleScrollView = titleScrollView; } #pragma mark - 添加内容滚动视图 - (void)setupContentScrollView { // 创建contentScrollView UIScrollView *contentScrollView = [[UIScrollView alloc] init]; contentScrollView.backgroundColor = [UIColor greenColor]; CGFloat y = CGRectGetMaxY(self.titleScrollView.frame); contentScrollView.frame = CGRectMake(0, y, self.view.bounds.size.width, self.view.bounds.size.height - y); [self.view addSubview:contentScrollView]; _contentScrollView = contentScrollView; //设置contentScrollView的属性 // 分页 self.contentScrollView.pagingEnabled = YES; // 弹簧 self.contentScrollView.bounces = NO; // 指示器 self.contentScrollView.showsHorizontalScrollIndicator = NO; //设置代理.目的:监听内容滚动视图 什么时候滚动完成 self.contentScrollView.delegate = self; } #pragma mark - UIScrollViewDelegate #pragma mark - 滚动完成的时候调用 -(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView { //获取当前的角标 NSInteger i = scrollView.contentOffset.x / ScreenW;//偏移量/屏幕宽度 //获取标题按钮 UIButton *titleBtn = self.titleBtns[i]; //1.选中标题 [self selButton:titleBtn]; //2.把对应子控制器的view添加上去 [self setupOneViewController:i]; } #pragma mark - 只要一滚动就需要字体渐变 - (void)scrollViewDidScroll:(UIScrollView *)scrollView { //字体缩放 1.缩放比例 2.缩放哪两个按钮 NSInteger page = scrollView.contentOffset.x / ScreenW; NSLog(@"%ld",page); NSInteger leftI = scrollView.contentOffset.x / ScreenW; NSInteger rightI = leftI + 1; // 获取左边的按钮 UIButton * leftBtn = self.titleBtns[leftI]; NSInteger count = self.titleBtns.count; // 获取右边的按钮 UIButton * rightBtn; if (rightI < count) { rightBtn = self.titleBtns[rightI]; } // 0~1 => 1~1.3 CGFloat scaleR = scrollView.contentOffset.x / ScreenW; scaleR -= leftI; CGFloat scaleL = 1- scaleR; NSLog(@"%f",scaleR); //缩放按钮 leftBtn.transform = CGAffineTransformMakeScale(scaleL * 0.3+1, scaleL * 0.3+1); rightBtn.transform = CGAffineTransformMakeScale(scaleR * 0.3+1, scaleR * 0.3+1); //FIXME:颜色渐变 //黑色变成红色 UIColor * rightColor = [UIColor colorWithRed:scaleR green:0 blue:0 alpha:1]; UIColor * leftColor = [UIColor colorWithRed:scaleL green:0 blue:0 alpha:1]; [rightBtn setTitleColor:rightColor forState:UIControlStateNormal]; [leftBtn setTitleColor:leftColor forState:UIControlStateNormal]; } /* 颜色:3种颜色通道组成: R:红 G 绿 B: k蓝 白色: 1 1 1 黑色: 0 0 0 红色: 1 0 0 */
代码:(已抽取)
BaseVC:
#import "ViewController.h" #define ScreenW [[UIScreen mainScreen] bounds].size.width #define ScreenH [[UIScreen mainScreen] bounds].size.height @interface ViewController ()<UIScrollViewDelegate> /** 标题按钮数组 */ @property (nonatomic, strong) NSMutableArray * titleBtns ; /** 上一次选中的按钮 */ @property (nonatomic, strong) UIButton * selectedBtn ; @property (nonatomic, weak) UIScrollView *titleScrollView; @property (nonatomic, weak) UIScrollView *contentScrollView; /** 判断是否初始化 */ @property (nonatomic, assign) BOOL isInitialize; @end @implementation ViewController -(void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; if (_isInitialize == NO) { //4.设置所有的标题 [self setupAllTitles]; _isInitialize = YES; } } -(NSMutableArray *)titleBtns { if (!_titleBtns) { _titleBtns = @[].mutableCopy; } return _titleBtns; } - (void)viewDidLoad { [super viewDidLoad]; self.navigationItem.title = @"网易新闻"; // 1.添加标题滚动视图 [self setupTitleScrollView]; // 2.添加内容滚动视图 [self setupContentScrollView]; //5.处理标题的点击 //7.FIXME:scrollView的额外滚动区域64 //iOS 7以后,导航控制器中scrollView顶部会添加64的额外滚动区域 self.automaticallyAdjustsScrollViewInsets = NO; } #pragma mark - 设置所有的标题 - (void)setupAllTitles { //已经把我们想要的内容展示上去 -> 展示的效果是否是我们想要的(调整细节) //1.标题的颜色 为黑色 //2.需要让titleScrollView可以滚动 //添加所有标题按钮 NSInteger count = self.childViewControllers.count; CGFloat btnW = 100; CGFloat btnH = self.titleScrollView.bounds.size.height; CGFloat btnX = 0; for (NSInteger i = 0; i<count; i++) { UIButton *titleBtn = [UIButton buttonWithType:UIButtonTypeCustom]; titleBtn.tag = i; //设置标题 UIViewController * vc = self.childViewControllers[i]; [titleBtn setTitle:vc.title forState:UIControlStateNormal]; //设置Frame btnX = i * btnW; titleBtn.frame = CGRectMake(btnX, 0, btnW, btnH); [titleBtn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; //监听按钮点击 [titleBtn addTarget:self action:@selector(titleClick:) forControlEvents:UIControlEventTouchUpInside]; //设置第一个按钮默认选中(头条默认选中) if (i == 0) { [self titleClick:titleBtn]; } //把标题按钮保存到对应的数组中 [self.titleBtns addObject:titleBtn]; [self.titleScrollView addSubview:titleBtn]; } //设置标题滚动范围 self.titleScrollView.contentSize = CGSizeMake(count * btnW, 0); self.titleScrollView.showsHorizontalScrollIndicator = NO; //6.设置内容的滚动范围 self.contentScrollView.contentSize = CGSizeMake(count * ScreenW, 0); } #pragma mark - 选中标题 - (void)selButton:(UIButton*)button { _selectedBtn.transform = CGAffineTransformIdentity; [_selectedBtn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; [button setTitleColor:[UIColor redColor] forState:UIControlStateNormal]; //_selectedBtn = button; //标题居中处理 [self setupTitleCenter:button]; //字体缩放: 形变 button.transform = CGAffineTransformMakeScale(1.3, 1.3); _selectedBtn = button; [self setupTitleScale]; } #pragma mark - 字体缩放 - (void)setupTitleScale { } #pragma mark - 标题居中处理 - (void)setupTitleCenter:(UIButton*)button { //本质:修改titleScrollView的偏移量 CGFloat offsetX = button.center.x - ScreenW * 0.5; NSLog(@"offsetX==%f",offsetX); //头条、热点(offsetX<0) if (offsetX < 0) { offsetX = 0; } //最大偏移量计算(解决订阅和科技) CGFloat maxOffsetX = self.titleScrollView.contentSize.width - ScreenW; if (offsetX > maxOffsetX) { offsetX = maxOffsetX; } [self.titleScrollView setContentOffset: CGPointMake(offsetX, 0) animated:YES]; //此时有bug:点击每个按钮都居中了,例如:头条、热点(offsetX<0) } #pragma mark - 添加一个子控制器的View - (void)setupOneViewController:(NSInteger)i { UIViewController * vc = self.childViewControllers[i]; //FIXME:防止重复加载View视图 // if (vc.viewIfLoaded) { // //iOS 9.0之后 // return; // } //FIXME:通用 if (vc.view.superview) { return; } CGFloat x = i * ScreenW; vc.view.frame = CGRectMake(x, 0, ScreenW, self.contentScrollView.bounds.size.height); [self.contentScrollView addSubview:vc.view]; } #pragma mark - 处理标题点击 - (void)titleClick:(UIButton*)button { //FIXME:8.监听内容视图滚动 NSInteger i = button.tag; //1.标题颜色变为红色 [self selButton:button]; //2.把对应子控制器的View添加上去 [self setupOneViewController:i]; //3.滚动到对应的位置 self.contentScrollView.contentOffset = CGPointMake(i * ScreenW, 0); } #pragma mark - 添加标题滚动视图 - (void)setupTitleScrollView { // 创建titleScrollView UIScrollView *titleScrollView = [[UIScrollView alloc] init]; //titleScrollView.backgroundColor = [UIColor redColor]; CGFloat y = self.navigationController.navigationBarHidden ? 20 : 64; titleScrollView.frame = CGRectMake(0, y, self.view.bounds.size.width, 44); [self.view addSubview:titleScrollView]; _titleScrollView = titleScrollView; } #pragma mark - 添加内容滚动视图 - (void)setupContentScrollView { // 创建contentScrollView UIScrollView *contentScrollView = [[UIScrollView alloc] init]; contentScrollView.backgroundColor = [UIColor greenColor]; CGFloat y = CGRectGetMaxY(self.titleScrollView.frame); contentScrollView.frame = CGRectMake(0, y, self.view.bounds.size.width, self.view.bounds.size.height - y); [self.view addSubview:contentScrollView]; _contentScrollView = contentScrollView; //设置contentScrollView的属性 // 分页 self.contentScrollView.pagingEnabled = YES; // 弹簧 self.contentScrollView.bounces = NO; // 指示器 self.contentScrollView.showsHorizontalScrollIndicator = NO; //设置代理.目的:监听内容滚动视图 什么时候滚动完成 self.contentScrollView.delegate = self; } #pragma mark - UIScrollViewDelegate #pragma mark - 滚动完成的时候调用 -(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView { //获取当前的角标 NSInteger i = scrollView.contentOffset.x / ScreenW;//偏移量/屏幕宽度 //获取标题按钮 UIButton *titleBtn = self.titleBtns[i]; //1.选中标题 [self selButton:titleBtn]; //2.把对应子控制器的view添加上去 [self setupOneViewController:i]; } #pragma mark - 只要一滚动就需要字体渐变 - (void)scrollViewDidScroll:(UIScrollView *)scrollView { //字体缩放 1.缩放比例 2.缩放哪两个按钮 NSInteger page = scrollView.contentOffset.x / ScreenW; NSLog(@"%ld",page); NSInteger leftI = scrollView.contentOffset.x / ScreenW; NSInteger rightI = leftI + 1; // 获取左边的按钮 UIButton * leftBtn = self.titleBtns[leftI]; NSInteger count = self.titleBtns.count; // 获取右边的按钮 UIButton * rightBtn; if (rightI < count) { rightBtn = self.titleBtns[rightI]; } // 0~1 => 1~1.3 CGFloat scaleR = scrollView.contentOffset.x / ScreenW; scaleR -= leftI; CGFloat scaleL = 1- scaleR; NSLog(@"%f",scaleR); //缩放按钮 leftBtn.transform = CGAffineTransformMakeScale(scaleL * 0.3+1, scaleL * 0.3+1); rightBtn.transform = CGAffineTransformMakeScale(scaleR * 0.3+1, scaleR * 0.3+1); //FIXME:颜色渐变 //黑色变成红色 UIColor * rightColor = [UIColor colorWithRed:scaleR green:0 blue:0 alpha:1]; UIColor * leftColor = [UIColor colorWithRed:scaleL green:0 blue:0 alpha:1]; [rightBtn setTitleColor:rightColor forState:UIControlStateNormal]; [leftBtn setTitleColor:leftColor forState:UIControlStateNormal]; }
网易VC
#import <UIKit/UIKit.h> #import "ViewController.h" NS_ASSUME_NONNULL_BEGIN @interface HKWYViewController : ViewController @end NS_ASSUME_NONNULL_END #import "HKWYViewController.h" #import "TopLineViewController.h" #import "HotViewController.h" #import "VideoViewController.h" #import "ScienceViewController.h" #import "SocietyViewController.h" #import "ReaderViewController.h" @interface HKWYViewController () @end @implementation HKWYViewController - (void)viewDidLoad { [super viewDidLoad]; //3.添加所有的子控制器 [self setupAllChildViewController]; } #pragma mark - 添加所有子控制器 - (void)setupAllChildViewController { //头条 TopLineViewController * vc = [TopLineViewController new]; vc.title = @"头条"; [self addChildViewController:vc]; //热点 HotViewController * hotVC = [HotViewController new]; hotVC.title = @"热点"; [self addChildViewController:hotVC]; //视频 VideoViewController * videoVC = [VideoViewController new]; videoVC.title = @"视频"; [self addChildViewController:videoVC]; //社会 SocietyViewController * societyVC = [SocietyViewController new]; societyVC.title = @"社会"; [self addChildViewController:societyVC]; //订阅 ReaderViewController * readerVC = [ReaderViewController new]; readerVC.title = @"订阅"; [self addChildViewController:readerVC]; //科技 ScienceViewController * scienceVC = [ScienceViewController new]; scienceVC.title = @"科技"; [self addChildViewController:scienceVC]; }