控制器切換,類似網易新聞的分頁菜單

github下載地址:https://github.com/SPStore/SPPageMenu
雖然這種工具網上已經多得不能再多了诺核,但是有兩大功能是它們所欠缺的:
1、 跟蹤器在跟蹤按鈕的時候裙士,跟蹤器的寬度能夠時刻跟隨按鈕寬度的改變而改變管毙。注意是時刻跟隨夭咬,不是滑動結束的時候才跟隨
2、菜單上按鈕的展示效果有3種:這主要依賴兩個屬性allowBeyondScreen和equalWidths卓舵,具體怎么設置參考demo
第一種:當按鈕數(shù)較多時掏湾,能夠以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方式監(jiān)聽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;

/** 快速創(chuàng)建菜單 */
+ (SPPageMenu *)pageMenuWithFrame:(CGRect)frame array:(NSArray *)array;


/*
 *  外界只要告訴該類index,內部會處理哪個button被選中
 */
- (void)selectButtonAtIndex:(NSInteger)index;

/*
 *  1.這個方法的功能是實現(xiàn)跟蹤器跟隨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;

/** 該數(shù)組中裝的是字符串 */
@property (nonatomic, strong) NSArray<NSString *> *menuTitleArray;
/** 跟蹤器(跟蹤button的下劃線) */
@property (nonatomic, weak) UIView *tracker;
/** 分割線 */
@property (nonatomic, weak) UIView *breakline;
/** 選中的button */
@property (nonatomic, strong) UIButton *selectedButton;

/** 裝載menuButton的數(shù)組 */
@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;
    
    // 創(chuàng)建分割線。這個分割線起著很重要的作用幢竹,起初我并沒有創(chuàng)建一個單獨的view作為分割線焕毫,而是利用backgroundView與其子控件scrollView之間的底部設置一定的間隙驶乾,造成分割線的效果级乐。但是這樣會產生一個很嚴重的隱患风科。如果不單獨創(chuàng)建分割線,那么backgroundView的第一個子控件將會是scrollView题山,而正好外界把這個封裝好的菜單添加為控制器view的第一個子控件顶瞳,在默認情況下愕秫,外界控制器的導航欄會將scrollView里面的內容往下壓64豫领。而此菜單欄的高度一般不會設置很高(<64)等恐,這就會導致scrollView里面的子控件看不見。因此囱稽,必須使得backgroundView的第一個子控件不是scrollView战惊。
    UIView *breakLine = [[UIView alloc] init];
    breakLine.backgroundColor = _breaklineColor;
    [self.backgroundView addSubview:breakLine];
    self.breakline = breakLine;
    
    // 創(chuàng)建承載菜單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
// 此方法是留給外界的接口吞获,以創(chuàng)建菜單欄
+ (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 {

    // 創(chuàng)建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);
    }

    // 創(chuàng)建跟蹤器
    [self creatTracker:self.scrollView.subviews.firstObject];
    
    [self menuBtnClick:self.scrollView.subviews.firstObject];
}

// 創(chuàng)建跟蹤器
- (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;
    
    // 執(zhí)行代理方法
    [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發(fā)生偏移(重點)
    [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發(fā)生偏移
- (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時不發(fā)生任何偏移痘番,置offSetX為0
    if (offSetX <= 0 || maxOffsetX <= 0) {
        offSetX = 0;
    }
    // 如果offSetX大于maxOffsetX,說明scrollView已經滑到盡頭,此時button也發(fā)生任何偏移了
    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的狀態(tài)決定著菜單中的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;
    // 以下兩個屬性是讓動畫保持動畫結束的狀態(tài)
    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

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市褪子,隨后出現(xiàn)的幾起案子量淌,更是在濱河造成了極大的恐慌,老刑警劉巖嫌褪,帶你破解...
    沈念sama閱讀 217,509評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件呀枢,死亡現(xiàn)場離奇詭異,居然都是意外死亡笼痛,警方通過查閱死者的電腦和手機裙秋,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評論 3 394
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來缨伊,“玉大人摘刑,你說我怎么就攤上這事】谭唬” “怎么了枷恕?”我有些...
    開封第一講書人閱讀 163,875評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長谭胚。 經常有香客問我徐块,道長未玻,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,441評論 1 293
  • 正文 為了忘掉前任蛹锰,我火速辦了婚禮深胳,結果婚禮上,老公的妹妹穿的比我還像新娘铜犬。我一直安慰自己舞终,他們只是感情好,可當我...
    茶點故事閱讀 67,488評論 6 392
  • 文/花漫 我一把揭開白布癣猾。 她就那樣靜靜地躺著敛劝,像睡著了一般。 火紅的嫁衣襯著肌膚如雪纷宇。 梳的紋絲不亂的頭發(fā)上夸盟,一...
    開封第一講書人閱讀 51,365評論 1 302
  • 那天,我揣著相機與錄音像捶,去河邊找鬼上陕。 笑死,一個胖子當著我的面吹牛拓春,可吹牛的內容都是我干的释簿。 我是一名探鬼主播,決...
    沈念sama閱讀 40,190評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼硼莽,長吁一口氣:“原來是場噩夢啊……” “哼庶溶!你這毒婦竟也來了?” 一聲冷哼從身側響起懂鸵,我...
    開封第一講書人閱讀 39,062評論 0 276
  • 序言:老撾萬榮一對情侶失蹤偏螺,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后匆光,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體套像,經...
    沈念sama閱讀 45,500評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,706評論 3 335
  • 正文 我和宋清朗相戀三年殴穴,在試婚紗的時候發(fā)現(xiàn)自己被綠了凉夯。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,834評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡采幌,死狀恐怖劲够,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情休傍,我是刑警寧澤征绎,帶...
    沈念sama閱讀 35,559評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響人柿,放射性物質發(fā)生泄漏柴墩。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,167評論 3 328
  • 文/蒙蒙 一凫岖、第九天 我趴在偏房一處隱蔽的房頂上張望江咳。 院中可真熱鬧,春花似錦哥放、人聲如沸歼指。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,779評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽踩身。三九已至,卻和暖如春社露,著一層夾襖步出監(jiān)牢的瞬間挟阻,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,912評論 1 269
  • 我被黑心中介騙來泰國打工峭弟, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留附鸽,地道東北人。 一個月前我還...
    沈念sama閱讀 47,958評論 2 370
  • 正文 我出身青樓瞒瘸,卻偏偏與公主長得像拒炎,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子挨务,可洞房花燭夜當晚...
    茶點故事閱讀 44,779評論 2 354

推薦閱讀更多精彩內容