美文网首页恩美第二个APP项目
控制器切换,类似网易新闻的分页菜单

控制器切换,类似网易新闻的分页菜单

作者: S型身材的猪 | 来源:发表于2016-09-11 20:42 被阅读188次

    github下载地址:https://github.com/SPStore/SPPageMenu
    虽然这种工具网上已经多得不能再多了,但是有两大功能是它们所欠缺的:
    1、 跟踪器在跟踪按钮的时候,跟踪器的宽度能够时刻跟随按钮宽度的改变而改变。注意是时刻跟随,不是滑动结束的时候才跟随
    2、菜单上按钮的展示效果有3种:这主要依赖两个属性allowBeyondScreen和equalWidths,具体怎么设置参考demo
    第一种:当按钮数较多时,能够以scrollView的形式左右滑动。
    第二种:所有按钮被限制在屏幕宽度之内,每个按钮等宽,等间距。
    第三种:所有按钮被限制在屏幕宽度之内,每个按钮根据文字自适应宽度,间距自适应。
    当然,我这个工具的功能仍然不够,很多菜单栏的右侧有一个功能按钮,点击这个功能按钮可以做其他事情,我这个工具暂时没加这个功能,我有时间一定会加上去。

    屏幕快照 2017-01-11 上午10.55.01.png

    .h文件

    #import <UIKit/UIKit.h>
    
    @class SPPageMenu;
    @protocol SPPageMenuDelegate <NSObject>
    
    @optional
    /** 
     * pageMenu:菜单对象
     * index:当前选中的button下标
     */
    - (void)pageMenu:(SPPageMenu *)pageMenu buttonClickedAtIndex:(NSInteger)index;
    /** 
     * pageMenu:菜单对象
     * fromIndex:上一个被选中的button下标
     * toIndex:当前被选中的button下标
     */
    - (void)pageMenu:(SPPageMenu *)pageMenu buttonClickedFromIndex:(NSInteger)fromIndex toIndex:(NSInteger)toIndex;
    @end
    
    
    @interface SPPageMenu : UIView
    
    @property (nonatomic, weak) id<SPPageMenuDelegate> delegate;
    
    /** block方式监听button被点击,外界可选择代理方式,也可以选择block方式 */
    @property (nonatomic, copy) void(^buttonClickedBlock)(NSInteger index);
    @property (nonatomic, copy) void(^buttonClicked_from_to_Block)(NSInteger fromIndex,  NSInteger toIndex);
    
    
    /** button之间的间距,默认为30 */
    @property (nonatomic, assign) CGFloat spacing;
    /** 第一个button的左边距,默认为间距的一半 */
    @property (nonatomic, assign) CGFloat firstButtonX;
    /** button的字体,默认为15号字体 */
    @property (nonatomic, strong) UIFont *buttonFont;
    /** 选中的button的字体颜色 */
    @property (nonatomic, strong) UIColor *selectedTitleColor;
    /** 未选中的button字体颜色,默认为黑色 */
    @property (nonatomic, strong) UIColor *unSelectedTitleColor;
    /** 分割线颜色,默认为亮灰色 */
    @property (nonatomic, strong) UIColor *breaklineColor;
    /** 是否显示分割线,默认为YES */
    @property (nonatomic, assign, getter=isShowBreakline) BOOL showBreakline;
    /** 是否显示跟踪器,默认为YES */
    @property (nonatomic, assign, getter=isShowTracker) BOOL showTracker;
    /** 跟踪器的高度,默认为2.0f */
    @property (nonatomic, assign) CGFloat trackerHeight;
    /** 跟踪器的颜色,默认与选中的button字体颜色一致 */
    @property (nonatomic, strong) UIColor *trackerColor;
    /** 是否开启动画,默认为NO */
    @property (nonatomic, assign, getter=isOpenAnimation) BOOL openAnimation;
    
    
    /** 当以下两个属性同时为NO时,spacing和firstButtonX属性将不受用户控制,这是合情合理的 */
    /** 是否允许超出屏幕,默认为YES,如果设置了NO,则菜单上的所有button都将示在在屏幕范围之内,并且默认等宽,整体居中显示 ,如果想要button根据文字自适应宽度,还要配合下面的“equalWidths”属性 */
    @property (nonatomic, assign, getter=isAllowBeyondScreen) BOOL allowBeyondScreen;
    /** 是否等宽,默认为YES,这个属性只有在屏幕范围之内的布局方式才有效 */
    @property (nonatomic, assign, getter=isEqualWidths) BOOL equalWidths;
    
    /** 快速创建菜单 */
    + (SPPageMenu *)pageMenuWithFrame:(CGRect)frame array:(NSArray *)array;
    
    
    /*
     *  外界只要告诉该类index,内部会处理哪个button被选中
     */
    - (void)selectButtonAtIndex:(NSInteger)index;
    
    /*
     *  1.这个方法的功能是实现跟踪器跟随scrollView的滚动而滚动;
     *  2.调用这个方法必须在scrollViewDidScrollView里面调;
     *  3.beginOffset:scrollView刚开始滑动的时候起始偏移量,在scrollViewWillBeginDragging:方法内部获取起始偏移量;
     *  4.scrollView:外面正在拖拽的scrollView;
     */
    - (void)moveTrackerFollowScrollView:(UIScrollView *)scrollView beginOffset:(CGFloat)beginOffset;
    @end
    

    .m文件

    #import "SPPageMenu.h"
    
    @interface SPPageMenu()
    
    @property (nonatomic, strong) UIView *backgroundView;
    @property (nonatomic, strong) UIScrollView *scrollView;
    
    /** 该数组中装的是字符串 */
    @property (nonatomic, strong) NSArray<NSString *> *menuTitleArray;
    /** 跟踪器(跟踪button的下划线) */
    @property (nonatomic, weak) UIView *tracker;
    /** 分割线 */
    @property (nonatomic, weak) UIView *breakline;
    /** 选中的button */
    @property (nonatomic, strong) UIButton *selectedButton;
    
    /** 装载menuButton的数组 */
    @property (nonatomic, strong) NSMutableArray<UIButton *> *menuButtonArray;
    
    /** 用来判断外界有没有设置firstButtonX */
    @property (nonatomic, assign, getter=isSettedFirstButtonX) BOOL settedFirstButtonX;
    /** 用来判断外界有没有设置spacing */
    @property (nonatomic, assign, getter=isSettedspacing) BOOL settedspacing;
    
    @end
    
    static NSInteger tagIndex = 2016;
    
    @implementation SPPageMenu
    
    #pragma mark - 初始化方法
    - (instancetype)initWithFrame:(CGRect)frame {
        self = [super initWithFrame:frame];
        if (self) {
            [self initialize];
        }
        return self;
    }
    
    - (instancetype)initWithCoder:(NSCoder *)aDecoder {
        if (self = [super initWithCoder:aDecoder]) {
            [self initialize];
        }
        return self;
    }
    
    // 初始化设置
    - (void)initialize {
        _buttonFont = [UIFont systemFontOfSize:15];
        _selectedTitleColor = [UIColor redColor];
        _unSelectedTitleColor = [UIColor blackColor];
        _breaklineColor = [UIColor lightGrayColor];
        _showBreakline = YES;
        _openAnimation = NO;
        _showTracker = YES;
        _trackerHeight = 2.0f;
        _trackerColor = _selectedTitleColor;
        _spacing = 30.0f;
        _firstButtonX = 0.5 * _spacing;
        _allowBeyondScreen = YES;
        _equalWidths = YES;
        
        [self setupSubView];
    }
    
    
    - (void)setupSubView {
        // 背景view
        UIView *backgroundView = [[UIView alloc] init];
        [self addSubview:backgroundView];
        self.backgroundView = backgroundView;
        
        // 创建分割线。这个分割线起着很重要的作用,起初我并没有创建一个单独的view作为分割线,而是利用backgroundView与其子控件scrollView之间的底部设置一定的间隙,造成分割线的效果。但是这样会产生一个很严重的隐患。如果不单独创建分割线,那么backgroundView的第一个子控件将会是scrollView,而正好外界把这个封装好的菜单添加为控制器view的第一个子控件,在默认情况下,外界控制器的导航栏会将scrollView里面的内容往下压64。而此菜单栏的高度一般不会设置很高(<64),这就会导致scrollView里面的子控件看不见。因此,必须使得backgroundView的第一个子控件不是scrollView。
        UIView *breakLine = [[UIView alloc] init];
        breakLine.backgroundColor = _breaklineColor;
        [self.backgroundView addSubview:breakLine];
        self.breakline = breakLine;
        
        // 创建承载菜单button的scrollView
        UIScrollView *scrollView = [[UIScrollView alloc] init];
        scrollView.showsVerticalScrollIndicator = NO;
        scrollView.showsHorizontalScrollIndicator = NO;
        [backgroundView addSubview:scrollView];
        self.scrollView = scrollView;
        scrollView.backgroundColor = [UIColor clearColor];
        
        [self layoutIfNeeded];
        
    }
    
    #pragma mark - public method
    // 此方法是留给外界的接口,以创建菜单栏
    + (SPPageMenu *)pageMenuWithFrame:(CGRect)frame array:(NSArray *)array {
        SPPageMenu *menu = [[SPPageMenu alloc] initWithFrame:frame];
        menu.menuTitleArray = array;
        return menu;
    }
    
    - (void)setMenuTitleArray:(NSArray<NSString *> *)menuTitleArray {
        _menuTitleArray = menuTitleArray;
        [self configureMenuButtonToScrollView];
    }
    
    
    #pragma mark - private method
    // 添加以及配置menubutton的相关属性
    - (void)configureMenuButtonToScrollView {
    
        // 创建button
        CGFloat lastMenuButtonMaxX = 0.0f;
        for (int i = 0; i < _menuTitleArray.count; i++) {
            UIButton *menuButton = [UIButton buttonWithType:UIButtonTypeCustom];
            menuButton.tag = tagIndex + i;
            [menuButton addTarget:self action:@selector(menuBtnClick:) forControlEvents:UIControlEventTouchUpInside];
            [menuButton setTitle:self.menuTitleArray[i] forState:UIControlStateNormal];
            menuButton.backgroundColor = [UIColor clearColor];
            menuButton.titleLabel.font = _buttonFont;
            menuButton.titleLabel.textAlignment = NSTextAlignmentCenter;
            [menuButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
            [menuButton sizeToFit];
            
            [self.scrollView addSubview:menuButton];
            [self.menuButtonArray addObject:menuButton];
            
            // 设置button的frame
            [self setupMenuButtonFrame:menuButton
                                  font:_buttonFont
                    lastMenuButtonMaxX:lastMenuButtonMaxX
                                 index:i];
            // 记录此时menuButton的最大x值
            lastMenuButtonMaxX = CGRectGetMaxX(menuButton.frame);
            // 设置scrollView的容量
            self.scrollView.contentSize = CGSizeMake(lastMenuButtonMaxX + _firstButtonX, 0);
        }
    
        // 创建跟踪器
        [self creatTracker:self.scrollView.subviews.firstObject];
        
        [self menuBtnClick:self.scrollView.subviews.firstObject];
    }
    
    // 创建跟踪器
    - (void)creatTracker:(UIButton *)button {
    
        UIView *tracker = [[UIView alloc] init];
        self.tracker = tracker;
        tracker.backgroundColor = _trackerColor;
        // 设置tracker的frame
        [self setupTrackerFrame:button];
     
        [self.scrollView addSubview:tracker];
        
    }
    
    // button的点击方法
    - (void)menuBtnClick:(UIButton *)button {
        // 是不是第一次进入,先默认为YES
        static BOOL firstEnter = YES;
        
        // 执行代理方法
        [self delegatePerformMethodWithFromIndex:self.selectedButton.tag - tagIndex
                                         toIndex:button.tag - tagIndex];
        // 回调block
        if (self.buttonClickedBlock) {
            self.buttonClickedBlock(button.tag - tagIndex);
        }
        
        if (self.buttonClicked_from_to_Block) {
            self.buttonClicked_from_to_Block(self.selectedButton.tag - tagIndex,button.tag - tagIndex);
        }
        
        // 如果点击的是同一个button,retun掉,因为后面的操作没必要重复。
        if (self.selectedButton == button) {
            return;
        }
        
        if (!firstEnter) {
            // 移动跟踪器
            [self moveTracker:button];
        }
        
        // 给button添加缩放动画
        if (self.openAnimation) {
           [self openAnimationWithLastSelectedButton:self.selectedButton currentSelectedButton:button];
        }
        
        // 设置button的字体颜色
        [self setupButtonTitleColor:button];
        
        // 记录当前选中的button
        self.selectedButton = button;
        
        
        
        // 让scrollView发生偏移(重点)
        [self moveScrollViewWithSelectedButton:button];
        
        
        firstEnter = NO;
    }
    
    - (void)setupButtonTitleColor:(UIButton *)button {
        
        [self.selectedButton setTitleColor:_unSelectedTitleColor forState:UIControlStateNormal];
        [button setTitleColor:_selectedTitleColor forState:UIControlStateNormal];
        
    }
    
    - (void)delegatePerformMethodWithFromIndex:(NSUInteger)fromIndex toIndex:(NSUInteger)toIndex {
        if ([self.delegate respondsToSelector:@selector(pageMenu:buttonClickedAtIndex:)]) {
            [self.delegate pageMenu:self buttonClickedAtIndex:toIndex];
        }
        if ([self.delegate respondsToSelector:@selector(pageMenu:buttonClickedFromIndex:toIndex:)]) {
            [self.delegate pageMenu:self buttonClickedFromIndex:fromIndex toIndex:toIndex];
        }
    }
    
    
    - (void)moveTracker:(UIButton *)button {
        [UIView animateWithDuration:0.25 animations:^{
            CGPoint trackerCenter = self.tracker.center;
            CGRect trackerFrame = self.tracker.frame;
            trackerCenter.x = button.center.x;
            trackerFrame.size.width = button.frame.size.width+_spacing;
            self.tracker.frame = trackerFrame;
            self.tracker.center = trackerCenter;
        }];
    }
    
    
    // 点击button让scrollView发生偏移
    - (void)moveScrollViewWithSelectedButton:(UIButton *)selectedButton {
        // CGRectGetMidX(self.scrollView.frame)指的是屏幕水平中心位置,它的值是固定不变的
        // 选中button的中心x值与scrollView的中心x值之差
        CGFloat offSetX = selectedButton.center.x - CGRectGetMidX(self.scrollView.frame);
        // scrollView的容量宽与自身宽之差(难点)
        CGFloat maxOffsetX = self.scrollView.contentSize.width - self.scrollView.frame.size.width;
        // 如果选中的button中心x值小于或者等于scrollView的中心x值,或者scrollView的容量宽度小于scrollView本身,此时点击button时不发生任何偏移,置offSetX为0
        if (offSetX <= 0 || maxOffsetX <= 0) {
            offSetX = 0;
        }
        // 如果offSetX大于maxOffsetX,说明scrollView已经滑到尽头,此时button也发生任何偏移了
        else if (offSetX > maxOffsetX){
            offSetX = maxOffsetX;
        }
        
        [self.scrollView setContentOffset:CGPointMake(offSetX, 0) animated:YES];
    }
    
    // 设置tracker的frame
    - (void)setupTrackerFrame:(UIButton *)button {
        CGFloat trackerH = _trackerHeight;
        CGFloat trackerW = button.frame.size.width+_spacing;
        CGFloat trackerX = button.frame.origin.x-0.5*_spacing;
        CGFloat trackerY = self.scrollView.frame.size.height - trackerH;
        self.tracker.frame = CGRectMake(trackerX, trackerY, trackerW, trackerH);
    }
    
    - (void)setupMenuButtonFrame:(UIButton *)menuButton font:(UIFont *)buttonFont lastMenuButtonMaxX:(CGFloat)lastMenuButtonMaxX index:(NSInteger)index {
        // canScroll的状态决定着菜单中的button的布局方式
        // menuButton的宽度
        CGFloat menuButtonW = [menuButton.titleLabel.text boundingRectWithSize:CGSizeMake(MAXFLOAT, 0) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName:_buttonFont} context:nil].size.width;
        CGFloat menuButtonH = self.scrollView.frame.size.height-1;
        CGFloat menuButtonX = (index == 0) ? _firstButtonX : (lastMenuButtonMaxX + _spacing);
        CGFloat menuButtonY = 0;
        menuButton.frame = CGRectMake(menuButtonX, menuButtonY, menuButtonW, menuButtonH);
    }
    
    - (void)resetMenuButtonFrame {
        __block CGFloat lastMenuButtonMaxX = 0.0f;
        
        if (_allowBeyondScreen) { // 允许超出屏幕
            [self.menuButtonArray enumerateObjectsUsingBlock:^(UIButton * _Nonnull menuButton, NSUInteger idx, BOOL * _Nonnull stop) {
                menuButton.titleLabel.font = self.buttonFont;
                [self setupMenuButtonFrame:menuButton font:_buttonFont lastMenuButtonMaxX:lastMenuButtonMaxX index:idx];
                lastMenuButtonMaxX = CGRectGetMaxX(menuButton.frame);
            }];
        } else { // 不允许超出屏幕
            if (_equalWidths) { // 等宽
                [self.menuButtonArray enumerateObjectsUsingBlock:^(UIButton * _Nonnull menuButton, NSUInteger idx, BOOL * _Nonnull stop) {
                    menuButton.titleLabel.font = self.buttonFont;
                    
                    CGFloat menuButtonW = (self.scrollView.frame.size.width-2*_firstButtonX-(self.menuTitleArray.count-1)*_spacing) / self.menuTitleArray.count;
                    CGFloat menuButtonH = self.scrollView.frame.size.height-1;
                    CGFloat menuButtonX = _firstButtonX + idx * (menuButtonW+_spacing);
                    CGFloat menuButtonY = 0;
                    menuButton.backgroundColor = [UIColor clearColor];
                    menuButton.frame = CGRectMake(menuButtonX, menuButtonY, menuButtonW, menuButtonH);
                }];
            } else {  // 不等宽(根据文字返回宽度,间距自适应)
                CGFloat menuButtonW_Sum = 0;
                NSMutableArray *menuButtonW_Array = [NSMutableArray array];
                
                CGFloat scrollViewWidth = self.scrollView.frame.size.width;
                
                NSInteger count = self.menuTitleArray.count;
                // 提前计算button宽
                for (NSString *title in self.menuTitleArray) {
                    CGFloat menuButtonW = [title boundingRectWithSize:CGSizeMake(MAXFLOAT, 0) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName:_buttonFont} context:nil].size.width;
                    
                    // 求出所有button的宽度之和,目的是算出间距
                    menuButtonW_Sum += menuButtonW;
                    [menuButtonW_Array addObject:@(menuButtonW)];
                }
                _spacing = (scrollViewWidth - menuButtonW_Sum) / (count+1);
                
                [self.menuButtonArray enumerateObjectsUsingBlock:^(UIButton * _Nonnull menuButton, NSUInteger idx, BOOL * _Nonnull stop) {
                    CGFloat menuButtonW = [menuButtonW_Array[idx] floatValue];
                    CGFloat menuButtonH = self.scrollView.frame.size.height-1;
                    CGFloat menuButtonX = _spacing + lastMenuButtonMaxX;
                    CGFloat menuButtonY = 0;
                    menuButton.frame = CGRectMake(menuButtonX, menuButtonY, menuButtonW, menuButtonH);
                    lastMenuButtonMaxX = CGRectGetMaxX(menuButton.frame);
                }];
            }
            
        
        }
        
        // 设置scrollView的容量
        self.scrollView.contentSize = CGSizeMake(lastMenuButtonMaxX + _firstButtonX, 0);
    }
    
    #pragma mark - 基本布局
    - (void)layoutSubviews {
        [super layoutSubviews];
    
        CGFloat w = self.frame.size.width;
        CGFloat h = self.frame.size.height;
        
        self.backgroundView.frame = CGRectMake(0, 0, w, h);
        
        self.breakline.frame = CGRectMake(0, h - 1, w, 1);
        
        // 减_breaklineHeight是为了有分割线的效果
        self.scrollView.frame = CGRectMake(0, 0, w, h);
    }
    
    
    #pragma mark - setter方法
    - (NSMutableArray *)menuButtonArray {
        if (!_menuButtonArray) {
            _menuButtonArray = [NSMutableArray array];
        }
        return _menuButtonArray;
    }
    
    // 设置button的间距
    - (void)setspacing:(CGFloat)spacing {
        _spacing = spacing;
        
        // 外界是否设置了spacing,如果能进来,说明设置了. 因此在内部不要调用该set方法
        _settedspacing = YES;
        
        // 如果外界没有设置firstButtonX,默认为新的spacing的一半
        if (!_settedFirstButtonX) {
            _firstButtonX = 0.5 * spacing;
        }
        
        // 重设button的frame
        [self resetMenuButtonFrame];
        
        UIButton *menuButton = self.menuButtonArray.firstObject;
        [self setupTrackerFrame:menuButton];
    }
    
    // 设置第一个button的左间距
    - (void)setFirstButtonX:(CGFloat)firstButtonX {
        _firstButtonX = firstButtonX;
        
        // 外界是否设置了firstButtonX,如果能进来,说明设置了. 因此在内部不要调用该set方法
        _settedFirstButtonX = YES;
        
        [self resetMenuButtonFrame];
        
        UIButton *menuButton = self.menuButtonArray.firstObject;
        [self setupTrackerFrame:menuButton];
    }
    
    // 设置字体,字体变了,button的frame和跟踪器的frame需要重新设置
    - (void)setButtonFont:(UIFont *)buttonFont {
        _buttonFont = buttonFont;
        
        [self resetMenuButtonFrame];
        
        UIButton *menuButton = self.menuButtonArray.firstObject;
        [self setupTrackerFrame:menuButton];
    }
    
    // 设置选中的button文字颜色
    - (void)setSelectedTitleColor:(UIColor *)selectedTitleColor {
        _selectedTitleColor = selectedTitleColor;
        
        [self.selectedButton setTitleColor:selectedTitleColor forState:UIControlStateNormal];
        self.tracker.backgroundColor = selectedTitleColor;
    }
    
    // 设置没有选中的button文字颜色
    - (void)setUnSelectedTitleColor:(UIColor *)unSelectedTitleColor {
        _unSelectedTitleColor = unSelectedTitleColor;
        
        for (UIButton *menuButton in self.menuButtonArray) {
            if (menuButton == _selectedButton) {
                continue;  // 跳过选中的那个button
            }
            [menuButton setTitleColor:unSelectedTitleColor forState:UIControlStateNormal];
        }
    }
    
    // 设置分割线颜色
    - (void)setBreaklineColor:(UIColor *)breaklineColor {
        _breaklineColor = breaklineColor;
        self.breakline.backgroundColor = breaklineColor;
    }
    
    // 设置是否显示分割线
    - (void)setShowBreakline:(BOOL)showBreakline {
        _showBreakline = showBreakline;
        self.breakline.hidden = !showBreakline;
    }
    
    // 设置是否显示跟踪器
    - (void)setShowTracker:(BOOL)showTracker {
        _showTracker = showTracker;
        self.tracker.hidden = !showTracker;
    }
    
    // 设置跟踪器的高度
    - (void)setTrackerHeight:(CGFloat)trackerHeight {
        _trackerHeight = trackerHeight;
        
        CGRect trackerFrame = self.tracker.frame;
        trackerFrame.size.height = trackerHeight;
        trackerFrame.origin.y = self.scrollView.frame.size.height - trackerHeight;
        self.tracker.frame = trackerFrame;
    }
    
    // 设置跟踪器的颜色
    - (void)setTrackerColor:(UIColor *)trackerColor {
        _trackerColor = trackerColor;
        self.tracker.backgroundColor = trackerColor;
    }
    
    // 设置是否开启动画
    - (void)setOpenAnimation:(BOOL)openAnimation {
        _openAnimation = openAnimation;
        if (openAnimation) {
            // 取出第一个button
            UIButton *menuButton = [self.menuButtonArray firstObject];
            // 如果外界开启了动画,则给第一个button加上放大动画。如果不这样做,外界开启动画后,第一个button是不会有放大效果的,只有点击了其它button之后才会有动画效果。
            CABasicAnimation *animation = [self enlargeAnimation];
            [menuButton.titleLabel.layer addAnimation:animation forKey:@"animation"];
        } else {
            // 遍历所有的button,如果外界关闭了动画,则将所有button上动画移除
            [self.menuButtonArray enumerateObjectsUsingBlock:^(UIButton*  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
                [obj.titleLabel.layer removeAllAnimations];
            }];
        }
    }
    
    // 返回放大的动画
    - (CABasicAnimation *)enlargeAnimation {
        CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
        animation.fromValue = [NSNumber numberWithFloat:1.0f];
        animation.toValue  = [NSNumber numberWithFloat:1.2f];
        animation.duration = 0.1;
        animation.repeatCount = 1;
        // 以下两个属性是让动画保持动画结束的状态
        animation.fillMode = kCAFillModeForwards;
        animation.removedOnCompletion = NO;
        animation.autoreverses = NO;
        return animation;
    }
    
    // 返回缩小动画
    - (CABasicAnimation *)narrowAnimation {
        CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
        animation.fromValue = [NSNumber numberWithFloat:1.2f];
        animation.toValue  = [NSNumber numberWithFloat:1.0f];
        animation.duration = 0.1;
        animation.repeatCount = 1;
        animation.fillMode = kCAFillModeForwards;
        animation.removedOnCompletion = NO;
        animation.autoreverses = NO;
        return animation;
    }
    
    // 开启动画
    - (void)openAnimationWithLastSelectedButton:(UIButton *)lastSelectedButton currentSelectedButton:(UIButton *)currentSelectedButton {
        CABasicAnimation *animation1 = [self enlargeAnimation];
        CABasicAnimation *animation2 = [self narrowAnimation];
        [lastSelectedButton.titleLabel.layer addAnimation:animation2 forKey:@"animation2"];
        [currentSelectedButton.titleLabel.layer addAnimation:animation1 forKey:@"animation1"];
    }
    
    // 是否允许超出屏幕
    - (void)setAllowBeyondScreen:(BOOL)allowBeyondScreen {
        _allowBeyondScreen = allowBeyondScreen;
        // 如果不能超出屏幕,且外界没有设置spacing,让间距默认为0.
        if (!self.allowBeyondScreen && !_settedspacing) {
            _spacing = 0.0f;
            _firstButtonX = 0.0f;
        }
        
        [self resetMenuButtonFrame];
        
        UIButton *menuButton = self.menuButtonArray.firstObject;
        [self setupTrackerFrame:menuButton];
    }
    
    // 是否让button等宽
    - (void)setEqualWidths:(BOOL)equalWidths {
        _equalWidths = equalWidths;
        
        [self resetMenuButtonFrame];
        
        UIButton *menuButton = self.menuButtonArray.firstObject;
        [self setupTrackerFrame:menuButton];
    }
    
    #pragma mark - 提供给外界的方法
    - (void)selectButtonAtIndex:(NSInteger)index{
        UIButton *selectedBtn = (UIButton *)self.menuButtonArray[index];
        [self menuBtnClick:selectedBtn];
    
    }
    
    - (void)moveTrackerFollowScrollView:(UIScrollView *)scrollView beginOffset:(CGFloat)beginOffset {
    
        // dragging是scrollView的一个属性, 如果为YES,说明用户中正在拖拽scrollView。
        // 如果用户在外面调用了这个方法 那么本方法会在点击菜单按钮的时候和用户拖拽外面的scrollView的时候调用.
        // 如果是用户点击菜单按钮进入的此方法,那dragging必然为NO(没有拖拽),并且没有在减速,此时直接retun,让跟踪器跟着菜单按钮走。
        // 如果是用户在外面拖拽scrollView而进入的此方法,那dragging必然为YES(正在拖拽),此时让跟踪器跟着scrollView的偏移量走
        // 当手指松开,decelerating属性为YES,表示scrolview正在减速
        if (!scrollView.dragging && !scrollView.decelerating) {
            return;
        }
        
        if (scrollView.contentOffset.x < 0 || scrollView.contentOffset.x > scrollView.contentSize.width-scrollView.bounds.size.width) {
            return;
        }
        
        CGFloat offSetX = scrollView.contentOffset.x;
        CGFloat tempProgress = offSetX / scrollView.bounds.size.width;
        CGFloat progress = tempProgress - floor(tempProgress);
        
        NSInteger oldIndex;
        NSInteger currentIndex;
        
        if (offSetX - beginOffset >= 0) { // 向左滑动
            oldIndex = offSetX / scrollView.bounds.size.width;
            currentIndex = oldIndex + 1;
            if (currentIndex >= self.menuTitleArray.count) {
                currentIndex = oldIndex - 1;
            }
            if (offSetX - beginOffset == scrollView.bounds.size.width) {// 滚动停止了
                progress = 1.0;
                currentIndex = oldIndex;
            }
        } else {  // 向右滑动
            currentIndex = offSetX / scrollView.bounds.size.width;
            oldIndex = currentIndex + 1;
            progress = 1.0 - progress;
            
        }
        
        [self moveTrackerWithProgress:progress fromIndex:oldIndex toIndex:currentIndex];
    }
    
    - (void)moveTrackerWithProgress:(CGFloat)progress fromIndex:(NSInteger)fromIndex toIndex:(NSInteger)toIndex {
        
        UIButton *oldButton = self.menuButtonArray[fromIndex];
        UIButton *currentButton = self.menuButtonArray[toIndex];
        
        CGFloat xDistance = currentButton.frame.origin.x - oldButton.frame.origin.x;
        CGFloat wDistance = currentButton.frame.size.width - oldButton.frame.size.width;
        
        
        CGRect newFrame = self.tracker.frame;
        newFrame.origin.x = oldButton.frame.origin.x + xDistance * progress - 0.5*_spacing;
        newFrame.size.width = oldButton.frame.size.width + wDistance * progress + _spacing;
        self.tracker.frame = newFrame;
    }
    
    
    @end
    

    github下载地址:https://github.com/SPStore/SPPageMenu

    相关文章

      网友评论

        本文标题:控制器切换,类似网易新闻的分页菜单

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