越來(lái)越多的客戶端會(huì)遇到需要實(shí)現(xiàn)類似“頭條”客戶端那樣的多視圖頁(yè)面左右來(lái)回滑動(dòng)切換惑申。
那么今天我們就來(lái)封裝一下這個(gè)功能,實(shí)現(xiàn)這個(gè)功能艺沼。
先看需求:
1奶栖、點(diǎn)擊標(biāo)題按鈕切換到對(duì)應(yīng)的表視圖上盐捷,或者左右滑動(dòng)視圖滑動(dòng)到相應(yīng)的視圖上棘捣。
2辜腺、可共用一個(gè)控制器、可實(shí)現(xiàn)不同的控制器。
網(wǎng)上很多多事有多少個(gè)標(biāo)題就有多少個(gè)控制器评疗,然后for一下加入到數(shù)組测砂,在把數(shù)組傳進(jìn)去創(chuàng)建。
那么壤巷,
今天我們封裝邑彪,就不用那么麻煩了瞧毙,直接可以共用一個(gè)控制器胧华。滿足很多客戶端只是請(qǐng)求的參數(shù)不一樣,但是頁(yè)面是長(zhǎng)得一樣樣的宙彪。
下面直接看封裝的類:
//
// XLBasePageController.h
// XLPackKnowledge
//
// Created by apple on 16/12/7.
// Copyright ? 2016年 apple. All rights reserved.
//
#import <UIKit/UIKit.h>
@class XLBasePageController;
#pragma mark View Pager Delegate
@protocol XLBasePageControllerDelegate <NSObject>
@optional
///返回當(dāng)前顯示的視圖控制器
-(void)viewPagerViewController:(XLBasePageController *)viewPager didFinishScrollWithCurrentViewController:(UIViewController *)viewController;
///返回當(dāng)前將要滑動(dòng)的視圖控制器
-(void)viewPagerViewController:(XLBasePageController *)viewPager willScrollerWithCurrentViewController:(UIViewController *)ViewController;
@end
#pragma mark View Pager DataSource
@protocol XLBasePageControllerDataSource <NSObject>
@required
-(NSInteger)numberViewControllersInViewPager:(XLBasePageController *)viewPager;
-(UIViewController *)viewPager:(XLBasePageController *)viewPager indexViewControllers:(NSInteger)index;
-(NSString *)viewPager:(XLBasePageController *)viewPager titleWithIndexViewControllers:(NSInteger)index;
@optional
///設(shè)置控制器標(biāo)題按鈕的樣式,不設(shè)置為默認(rèn)
-(UIButton *)viewPager:(XLBasePageController *)viewPager titleButtonStyle:(NSInteger)index;
-(CGFloat)heightForTitleViewPager:(XLBasePageController *)viewPager;
///預(yù)留數(shù)據(jù)源
-(UIView *)headerViewForInViewPager:(XLBasePageController *)viewPager;
-(CGFloat)heightForHeaderViewPager:(XLBasePageController *)viewPager;
@end
@interface XLBasePageController : UIViewController
@property (nonatomic,weak) id<XLBasePageControllerDataSource> dataSource;
@property (nonatomic,weak) id<XLBasePageControllerDelegate> delegate;
///刷新
-(void)reloadScrollPage;
///默認(rèn)選中
@property(nonatomic,assign) NSInteger selectIndex;
///按鈕下劃線的高度 字體大小 默認(rèn)顏色 選中顏色
@property (nonatomic,assign) CGFloat lineWidth;
@property (nonatomic,strong) UIFont *titleFont;
@property (nonatomic,strong) UIColor *defaultColor;
@property (nonatomic,strong) UIColor *chooseColor;
@end
#pragma mark 標(biāo)題按鈕
@interface XLBasePageTitleButton : UIButton
@property (nonatomic,assign) CGFloat buttonlineWidth;
@end
//
// XLBasePageController.m
// XLPackKnowledge
//
// Created by apple on 16/12/7.
// Copyright ? 2016年 apple. All rights reserved.
//
#import "XLBasePageController.h"
@interface XLBasePageController ()<UIPageViewControllerDelegate,UIPageViewControllerDataSource>
{
NSInteger totalVC; //VC的總數(shù)量
NSArray *VCArray; //存放VC的數(shù)組
NSArray *buttonArray; //存放VC Button的數(shù)組
UIView *headerView; //頭部視圖
CGRect oldRect; //用來(lái)保存title布局的Rect
XLBasePageTitleButton *oldButton;
NSInteger currentVCIndex; //當(dāng)前VC索引
}
@property (nonatomic,strong) UIPageViewController *pageViewController;
@property (nonatomic,strong) UIScrollView *titleBGScroll;
@end
@implementation XLBasePageController
- (void)viewDidLoad {
[super viewDidLoad];
self.lineWidth = self.lineWidth>0?self.lineWidth:1.5;
self.titleFont = self.titleFont?self.titleFont:[UIFont systemFontOfSize:14.0];
self.defaultColor = self.defaultColor?self.defaultColor:[UIColor blackColor];
self.chooseColor = self.chooseColor?self.chooseColor:[UIColor redColor];
[self addChildViewController:self.pageViewController];
[self.view addSubview:self.pageViewController.view];
[self.view addSubview:self.titleBackground];
}
-(UIPageViewController *)pageViewController
{
if (!_pageViewController) {
_pageViewController = [[UIPageViewController alloc] initWithTransitionStyle:UIPageViewControllerTransitionStyleScroll navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal options:nil];
_pageViewController.delegate = self;
_pageViewController.dataSource = self;
}
return _pageViewController;
}
-(UIScrollView *)titleBackground
{
if (!_titleBGScroll) {
_titleBGScroll = [[UIScrollView alloc] init];
_titleBGScroll.backgroundColor = [UIColor whiteColor];
_titleBGScroll.showsHorizontalScrollIndicator = NO;
_titleBGScroll.showsVerticalScrollIndicator = NO;
}
return _titleBGScroll;
}
-(void)setDataSource:(id<XLBasePageControllerDataSource>)dataSource
{
_dataSource = dataSource;
//[self reloadScrollPage];
}
-(void)reloadScrollPage
{
if ([self.dataSource respondsToSelector:@selector(numberViewControllersInViewPager:)]) {
oldRect = CGRectZero;
totalVC = [self.dataSource numberViewControllersInViewPager:self];
NSMutableArray *VCList = [NSMutableArray array];
NSMutableArray *buttonList = [NSMutableArray array];
for (int i = 0; i<totalVC; i++) {
if ([self.dataSource respondsToSelector:@selector(viewPager:indexViewControllers:)]) {
id viewcontroller = [self.dataSource viewPager:self indexViewControllers:i];
if ([viewcontroller isKindOfClass:[UIViewController class]]) {
[VCList addObject:viewcontroller];
}
}
if ([self.dataSource respondsToSelector:@selector(viewPager:titleWithIndexViewControllers:)]) {
NSString *buttonTitle = [self.dataSource viewPager:self titleWithIndexViewControllers:i];
if (buttonArray.count > i) {
[[buttonArray objectAtIndex:i] removeFromSuperview];
}
UIButton *button;
if ([self.dataSource respondsToSelector:@selector(viewPager:titleButtonStyle:)])
{
if ([[self.dataSource viewPager:self titleButtonStyle:i] isKindOfClass:[UIButton class]]) {
button = [self.dataSource viewPager:self titleButtonStyle:i];
}
}else{
XLBasePageTitleButton *autoButton = [[XLBasePageTitleButton alloc] init];
autoButton.buttonlineWidth = self.lineWidth;
[autoButton.titleLabel setFont:self.titleFont];
[autoButton setTitleColor:self.defaultColor forState:UIControlStateNormal];
[autoButton setTitleColor:self.chooseColor forState:UIControlStateSelected];
button = autoButton;
}
[button addTarget:self action:@selector(titleButtonClick:) forControlEvents:UIControlEventTouchUpInside];
button.frame = CGRectMake(oldRect.origin.x+oldRect.size.width, 0, [self textString:buttonTitle withFontHeight:20], [self.dataSource respondsToSelector:@selector(heightForTitleViewPager:)]?[self.dataSource heightForTitleViewPager:self]:0);
oldRect = button.frame;
[button setTitle:buttonTitle forState:UIControlStateNormal];
[buttonList addObject:button];
[_titleBGScroll addSubview:button];
if (i == self.selectIndex) {
oldButton = [buttonList objectAtIndex:self.selectIndex];
oldButton.selected = YES;
}
}
}
//當(dāng)所有按鈕尺寸小于屏幕寬度的時(shí)候要重新布局
if (buttonList.count && ((UIButton *)buttonList.lastObject).frame.origin.x + ((UIButton *)buttonList.lastObject).frame.size.width<self.view.frame.size.width)
{
oldRect = CGRectZero;
CGFloat padding = self.view.frame.size.width-(((UIButton *)buttonList.lastObject).frame.origin.x + ((UIButton *)buttonList.lastObject).frame.size.width);
for (XLBasePageTitleButton *button in buttonList) {
button.frame = CGRectMake(oldRect.origin.x+oldRect.size.width, 0,button.frame.size.width+padding/buttonList.count, [self.dataSource respondsToSelector:@selector(heightForTitleViewPager:)]?[self.dataSource heightForTitleViewPager:self]:0);
oldRect = button.frame;
}
}
buttonArray = [buttonList copy];
VCArray = [VCList copy];
}
if ([self.dataSource respondsToSelector:@selector(headerViewForInViewPager:)]) {
[headerView removeFromSuperview];
headerView = [self.dataSource headerViewForInViewPager:self];
[self.view addSubview:headerView];
}
if (VCArray.count) {
[_pageViewController setViewControllers:@[VCArray[self.selectIndex]] direction:UIPageViewControllerNavigationDirectionForward animated:YES completion:nil];
}
}
-(void)titleButtonClick:(XLBasePageTitleButton *)sender
{
oldButton.selected = NO;
sender.selected = YES;
oldButton = sender;
NSInteger index = [buttonArray indexOfObject:sender];
[_pageViewController setViewControllers:@[VCArray[index]] direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:nil];
[self scrollViewOffset:sender];
}
-(void)titleButtonConvert:(XLBasePageTitleButton *)sender
{
oldButton.selected = NO;
sender.selected = YES;
oldButton = sender;
[self scrollViewOffset:sender];
}
-(void)scrollViewOffset:(UIButton *)button
{
if (!(_titleBGScroll.contentSize.width>CGRectGetWidth(self.view.frame))) {
return;
}
if (CGRectGetMidX(button.frame)>CGRectGetMidX(self.view.frame)) {
if (_titleBGScroll.contentSize.width<CGRectGetMaxX(self.view.frame)/2+CGRectGetMidX(button.frame)) {
[_titleBGScroll setContentOffset:CGPointMake(_titleBGScroll.contentSize.width-CGRectGetWidth(self.view.frame), 0) animated:YES];
}
else{
[_titleBGScroll setContentOffset:CGPointMake(CGRectGetMidX(button.frame)-CGRectGetWidth(self.view.frame)/2, 0) animated:YES];
}
}
else{
[_titleBGScroll setContentOffset:CGPointMake(0, 0) animated:YES];
}
}
#pragma mark -UIPageViewControllerDelegate
- (void)pageViewController:(UIPageViewController *)pageViewController didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray<UIViewController *> *)previousViewControllers transitionCompleted:(BOOL)completed
{
if (completed) {
if (currentVCIndex != [VCArray indexOfObject:previousViewControllers[0]]) {
[self chooseTitleIndex:currentVCIndex];
if ([self.delegate respondsToSelector:@selector(viewPagerViewController:didFinishScrollWithCurrentViewController:)]) {
[self.delegate viewPagerViewController:self didFinishScrollWithCurrentViewController:[VCArray objectAtIndex:currentVCIndex]];
}
}
}
}
- (void)pageViewController:(UIPageViewController *)pageViewController willTransitionToViewControllers:(NSArray<UIViewController *> *)pendingViewControllers
{
currentVCIndex = [VCArray indexOfObject:pendingViewControllers[0]];
if ([self.delegate respondsToSelector:@selector(viewPagerViewController:willScrollerWithCurrentViewController:)]) {
[self.delegate viewPagerViewController:self willScrollerWithCurrentViewController:pageViewController.viewControllers[0]];
}
}
#pragma mark -UIPageViewControllerDataSource
- (nullable UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController
{
NSInteger index = [VCArray indexOfObject:viewController];
if (index == 0 || index == NSNotFound) {
return nil;
}else{
return VCArray[--index];
}
}
- (nullable UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController
{
NSInteger index = [VCArray indexOfObject:viewController];
if (index == VCArray.count-1 || index == NSNotFound) {
return nil;
}else{
return VCArray[++index];
}
}
-(void)chooseTitleIndex:(NSInteger)index
{
[self titleButtonConvert:buttonArray[index]];
}
-(void)viewDidLayoutSubviews
{
headerView.frame = CGRectMake(0, self.topLayoutGuide.length, self.view.frame.size.width,[self.dataSource respondsToSelector:@selector(heightForHeaderViewPager:)]?[self.dataSource heightForHeaderViewPager:self]:0);
_titleBGScroll.frame = CGRectMake(0, (headerView.frame.size.height)?headerView.frame.origin.y+headerView.frame.size.height:self.topLayoutGuide.length, self.view.frame.size.width,[self.dataSource respondsToSelector:@selector(heightForTitleViewPager:)]?[self.dataSource heightForTitleViewPager:self]:0);
if (buttonArray.count) {
_titleBGScroll.contentSize = CGSizeMake(((UIButton *)buttonArray.lastObject).frame.size.width+((UIButton *)buttonArray.lastObject).frame.origin.x, _titleBGScroll.frame.size.height);
}
_pageViewController.view.frame = CGRectMake(0, _titleBGScroll.frame.origin.y+_titleBGScroll.frame.size.height, self.view.frame.size.width, self.view.frame.size.height-(_titleBGScroll.frame.origin.y+_titleBGScroll.frame.size.height));
}
#pragma mark 計(jì)算字體寬度
-(CGFloat)textString:(NSString *)text withFontHeight:(CGFloat)height
{
CGFloat padding = 20;
NSDictionary *fontAttribute = @{NSFontAttributeName : self.titleFont};
CGSize fontSize = [text boundingRectWithSize:CGSizeMake(MAXFLOAT, height) options:NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading attributes:fontAttribute context:nil].size;
return fontSize.width+padding;
}
@end
#pragma mark 標(biāo)題按鈕的屬性設(shè)置
@implementation XLBasePageTitleButton
- (instancetype)init
{
self = [super init];
if (self) {
}
return self;
}
-(void)drawRect:(CGRect)rect
{
if (self.selected) {
CGFloat lineWidth = 1.0;
CGColorRef color = self.titleLabel.textColor.CGColor;
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextSetStrokeColorWithColor(ctx, color);
CGContextSetLineWidth(ctx, lineWidth);
CGContextMoveToPoint(ctx, 0, self.frame.size.height-lineWidth);
CGContextAddLineToPoint(ctx, self.frame.size.width, self.frame.size.height-lineWidth);
CGContextStrokePath(ctx);
}
}
@end
項(xiàng)目需求矩动,在預(yù)留頭部視圖不隨滑動(dòng)而滑動(dòng)的效果,如果不需要释漆,可以不實(shí)現(xiàn)該方法悲没,默認(rèn)是無(wú)頭部視圖的,效果圖:
那么如何使用呢男图?很簡(jiǎn)單示姿,新建一個(gè)控制器繼承:XLBasePageController,然后實(shí)現(xiàn):
XLBasePageControllerDelegate逊笆、XLBasePageControllerDataSource
//
// BaseScrollListVC.m
// XLPackKnowledge
//
// Created by apple on 16/12/7.
// Copyright ? 2016年 apple. All rights reserved.
//
#import "BaseScrollListVC.h"
#import "ListVC.h"
#import "DetailVC.h"
@interface BaseScrollListVC () <XLBasePageControllerDelegate,XLBasePageControllerDataSource>
@property (nonatomic,strong) NSArray *titleArray;
@property (nonatomic,strong) UIView *headerView;
@end
@implementation BaseScrollListVC
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
_titleArray = @[@"全部",@"推薦",@"熱點(diǎn)",@"附近",@"訂閱",@"問(wèn)答",@"社會(huì)",@"體育",@"財(cái)經(jīng)"];
self.delegate = self;
self.dataSource = self;
self.lineWidth = 2.0;//選中下劃線寬度
self.titleFont = [UIFont systemFontOfSize:16.0];
self.defaultColor = [UIColor blackColor];//默認(rèn)字體顏色
self.chooseColor = [UIColor blueColor];//選中字體顏色
self.selectIndex = 1;//默認(rèn)選中第幾頁(yè)
[self reloadScrollPage];
}
-(NSInteger)numberViewControllersInViewPager:(XLBasePageController *)viewPager
{
return _titleArray.count;
}
-(UIViewController *)viewPager:(XLBasePageController *)viewPager indexViewControllers:(NSInteger)index
{
if (index != 2) {
ListVC *listVC = [[ListVC alloc] init];
listVC.title = _titleArray[index];
listVC.index = index;
return listVC;
}else{
DetailVC *detailVC = [[DetailVC alloc] init];
detailVC.title = _titleArray[index];
detailVC.index = index;
return detailVC;
}
}
-(CGFloat)heightForTitleViewPager:(XLBasePageController *)viewPager
{
return 50;
}
-(NSString *)viewPager:(XLBasePageController *)viewPager titleWithIndexViewControllers:(NSInteger)index
{
return self.titleArray[index];
}
-(void)viewPagerViewController:(XLBasePageController *)viewPager didFinishScrollWithCurrentViewController:(UIViewController *)viewController
{
self.title = viewController.title;
}
#pragma mark 預(yù)留--可不實(shí)現(xiàn)
-(UIView *)headerViewForInViewPager:(XLBasePageController *)viewPager
{
return self.headerView;
}
-(CGFloat)heightForHeaderViewPager:(XLBasePageController *)viewPager
{
return 100;
}
-(UIView *)headerView
{
if (_headerView == nil) {
_headerView = [[UIView alloc] init];
_headerView.backgroundColor = [UIColor colorWithRed:120/255.0f green:210/255.0f blue:249/255.0f alpha:1];
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 10, self.view.bounds.size.width, 40)];
label.textColor = [UIColor grayColor];
label.font = [UIFont systemFontOfSize:12.0];
label.text = @"固定的頭View,不可跟隨滑動(dòng),可不顯示";
label.textAlignment = NSTextAlignmentCenter;
[_headerView addSubview:label];
}
return _headerView;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
下面是幾個(gè)可隨便自定義的屬性。
self.lineWidth = 2.0;//選中下劃線寬度
self.titleFont = [UIFont systemFontOfSize:16.0];
self.defaultColor = [UIColor blackColor];//默認(rèn)字體顏色
self.chooseColor = [UIColor blueColor];//選中字體顏色
self.selectIndex = 1;//默認(rèn)選中第幾頁(yè)
Demo下載地址难裆,歡迎stars --> XLBasePage