美文网首页iOS快速开发总结搬砖iOS Developer
iOS开发之多视图页面(可共用同一个页面)左右来回滑动切换视图的

iOS开发之多视图页面(可共用同一个页面)左右来回滑动切换视图的

作者: 键盘上的演绎者 | 来源:发表于2016-12-07 20:50 被阅读6355次
    多视图页面左右来回滑动切换效果

    越来越多的客户端会遇到需要实现类似“头条”客户端那样的多视图页面左右来回滑动切换。
    那么今天我们就来封装一下这个功能,实现这个功能。

    先看需求:
    1、点击标题按钮切换到对应的表视图上,或者左右滑动视图滑动到相应的视图上。
    2、可共用一个控制器、可实现不同的控制器。

    网上很多多事有多少个标题就有多少个控制器,然后for一下加入到数组,在把数组传进去创建。
    那么,
    今天我们封装,就不用那么麻烦了,直接可以共用一个控制器。满足很多客户端只是请求的参数不一样,但是页面是长得一样样的。

    左右滑动切换视图

    下面直接看封装的类:

    //
    //  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
    ///返回当前显示的视图控制器
    -(void)viewPagerViewController:(XLBasePageController *)viewPager didFinishScrollWithCurrentViewController:(UIViewController *)viewController;
    ///返回当前将要滑动的视图控制器
    -(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
    ///设置控制器标题按钮的样式,不设置为默认
    -(UIButton *)viewPager:(XLBasePageController *)viewPager titleButtonStyle:(NSInteger)index;
    -(CGFloat)heightForTitleViewPager:(XLBasePageController *)viewPager;
    
    ///预留数据源
    -(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;
    
    ///默认选中
    @property(nonatomic,assign) NSInteger selectIndex;
    
    ///按钮下划线的高度 字体大小 默认颜色  选中颜色
    @property (nonatomic,assign) CGFloat lineWidth;
    @property (nonatomic,strong) UIFont *titleFont;
    @property (nonatomic,strong) UIColor *defaultColor;
    @property (nonatomic,strong) UIColor *chooseColor;
    
    @end
    
    #pragma mark 标题按钮
    @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的总数量
        NSArray *VCArray;          //存放VC的数组
        NSArray *buttonArray;      //存放VC Button的数组
        UIView *headerView;        //头部视图
        CGRect oldRect;            //用来保存title布局的Rect
        XLBasePageTitleButton *oldButton;
        NSInteger currentVCIndex;  //当前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;
                    }
                }
            }
            //当所有按钮尺寸小于屏幕宽度的时候要重新布局
            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 计算字体宽度
    -(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 标题按钮的属性设置
    @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
    

    项目需求,在预留头部视图不随滑动而滑动的效果,如果不需要,可以不实现该方法,默认是无头部视图的,效果图:

    头部视图是固定的咯

    那么如何使用呢?很简单,新建一个控制器继承:XLBasePageController,然后实现:
    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 = @[@"全部",@"推荐",@"热点",@"附近",@"订阅",@"问答",@"社会",@"体育",@"财经"];
        
        self.delegate = self;
        self.dataSource = self;
        
        self.lineWidth = 2.0;//选中下划线宽度
        self.titleFont = [UIFont systemFontOfSize:16.0];
        self.defaultColor = [UIColor blackColor];//默认字体颜色
        self.chooseColor = [UIColor blueColor];//选中字体颜色
        self.selectIndex = 1;//默认选中第几页
        
        [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 预留--可不实现
    
    -(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,不可跟随滑动,可不显示";
            label.textAlignment = NSTextAlignmentCenter;
            [_headerView addSubview:label];
        }
        return _headerView;
    }
    
    - (void)didReceiveMemoryWarning {
        [super didReceiveMemoryWarning];
        // Dispose of any resources that can be recreated.
    }
    
    @end
    
    可不设置,都有默认值了

    下面是几个可随便自定义的属性。

        self.lineWidth = 2.0;//选中下划线宽度
        self.titleFont = [UIFont systemFontOfSize:16.0];
        self.defaultColor = [UIColor blackColor];//默认字体颜色
        self.chooseColor = [UIColor blueColor];//选中字体颜色
        self.selectIndex = 1;//默认选中第几页
    

    Demo下载地址,欢迎stars --> XLBasePage

    相关文章

      网友评论

      • 6bbba3902a7a:标题的点击事件是哪个,?谢啦
      • 6bbba3902a7a:标题的点击事件是哪个?
      • 6bbba3902a7a:如果标题是由后台接口返回的时候,上面的标题不能滑动,左右滑动的时候上面的标题不能正常的显示了
      • 6bbba3902a7a:可以留个qq吗?
        键盘上的演绎者:@小欣_27b2 834095283
      • 6bbba3902a7a:如果我创建的VC,不继承XLBasePageController。可以吗?
        6bbba3902a7a:@键盘上的演绎者 还有个问题,就是APP刚运行的时候,每次滑动都会走到ListVC, 然后之前走的标题,回来再走到这个标题的时候 就不会在ListVC了。问题是 如果停到某个标题下面的时候,刷新加载更多的,需要传标题名字和index,我怎么知道我当前滑到了哪个标题的下面呢. 956707454 这是我的qq。麻烦你了
        键盘上的演绎者:@小欣_27b2 你可以自己在进行封装 这样就可以不继承了 只是我多个地方要用到就封装成继承的
      • iDog:老铁 我在使用过程中出现了预加载的问题 你有注意到这个问题吗
        键盘上的演绎者:@iDog 可以加我,详细描述一下,我没遇到
      • 2b71a369532c:刚需啊,谢谢。
      • 5b726ed578ce:求 demo 1171246504@qq.com
        键盘上的演绎者:@Marshall_Chou https://github.com/zhanghaa 喜欢的话,麻烦stars 谢谢
      • baozi1992:qq邮箱 gml1992@vip.qq.com
        键盘上的演绎者:@baozi1992 https://github.com/zhanghaa 喜欢的话,麻烦stars 谢谢
      • baozi1992:你好,请问可以加下qq发个demo么,谢谢了
      • 61cc77d04048:求demo,项目比较急,谢谢了hkqzzzhang@qq.com
        键盘上的演绎者:@黑猫警长kyo https://github.com/zhanghaa 喜欢的话,麻烦stars 谢谢
      • Monster_Lai:你好 我现在写了一个这种页面 但是headview要跟着滑动 这样左右滑动就会有一部分空白,怎么实现上下滑动其中一页 也通知其它页面也滑动到这个位置呢?
        键盘上的演绎者:@键盘上的演绎者 https://github.com/zhanghaa 喜欢的话,麻烦stars 谢谢
        键盘上的演绎者:@Monster_Lai 不明白你的意思,能详细描述一下吗?或者加我扣,谢谢
      • 4ae4223a0fae:求demo,我项目也需要这个,加急
        键盘上的演绎者:已发,谢谢您的喜欢

      本文标题:iOS开发之多视图页面(可共用同一个页面)左右来回滑动切换视图的

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