WYSegmentView.m 13.5 KB
//
//  WYSegmentView.h
//  GengMei
//
//  Created by wangyang on 13-8-9.
//  Copyright (c) 2015 Gengmei. All rights reserved.
//

#import "WYSegmentView.h"
#import <QuartzCore/QuartzCore.h>
#import <Masonry/Masonry.h>
#import "UIView+Layout.h"
#import <Constant.h>
#import "UIView+LineWithAutolayout.h"
#import "GMFont.h"
#import <UIColor+GMTheme.h>
#import "GMButton.h"
#import "UIDevice+Resolutions.h"
#import "UIButton+WebCache.h"
#import "UIImage+MultiFormat.h"
#import "UIImageView+WebCache.h"
#define BUTTON_TAG      1010

@interface WYSegmentView ()
{
    UIView *_indicatorView;
    UIView *_container;
}
@property (nonatomic, assign) NSInteger selectedSegmentIndex;

@end

@implementation WYSegmentView
- (void)setup
{
    [super setup];
    _bothEndPadding = ([UIDevice resolutionType] <= iPhone40Inches) ? 12 : 16;
    _bottomLine = [self addBottomLine];
    
    // scroll view
    _scrollView = [UIScrollView new];
    [self addSubview:_scrollView];
    [_scrollView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.edges.insets(UIEdgeInsetsZero);
    }];
    
    // container
    _container = [UIView new];
    [_scrollView addSubview:_container];
    [_container mas_makeConstraints:^(MASConstraintMaker *make) {
        make.edges.insets(UIEdgeInsetsZero);
    }];
    
    // 属性初始化
    _scrollView.showsHorizontalScrollIndicator = NO;
    _scrollView.scrollsToTop = NO;
    self.indicatorHorizonalPadding = 7.5 * ONE_PIXEL;
    self.titleTextAttributes = @{NSFontAttributeName : [UIFont gmFont:15],
                                 NSForegroundColorAttributeName : UIColor.whiteColor};
    self.selectedTitleTextAttributes = @{NSFontAttributeName : [UIFont gmFont:15],
                                         NSForegroundColorAttributeName : [UIColor auxiliaryTextGreen]};
    self.showIndicator = NO;
    self.indicatorColor = UIColor.auxiliaryTextGreen;
    self.widthStyle = WYSegmentViewWidthStyleDynamic;
    _selectedSegmentIndex = 0;
    
    // 添加指示器
    [self setupIndicatorView];
}

- (void)setupIndicatorView {
    
    _indicatorView = [UIView new];
    _indicatorView.backgroundColor = self.indicatorColor;
    [_scrollView insertSubview:_indicatorView atIndex:0];
    
    __weak __typeof(self)weakSelf = self;
    self.updateIndicatorFrame = ^(UIView *indicator, UIButton *button) {
        // 在 _sectionTitles 还没有数据时,button 并没有被添加,会是 nil, 所以添加如下判断
        if (button == nil || indicator == nil) {
            return;
        }
        // 改变indicator
        indicator.frame = CGRectMake(button.titleLabel.width * 1 / 6, indicator.superview.height - 9, button.titleLabel.width * 2 / 3, 3);
        indicator.centerX = button.centerX;
        indicator.layer.cornerRadius = 1 + ONE_PIXEL;
        indicator.layer.masksToBounds = YES;
        indicator.layer.shadowColor = weakSelf.indicatorColor.CGColor;
        indicator.layer.shadowOpacity = 0.35;
        indicator.layer.shadowOffset = CGSizeMake(0,1);
        indicator.layer.shadowRadius = 1;
        indicator.clipsToBounds = NO;
    };
}

- (void)setupSegments {
    [_container removeAllSubviews];

    // button间距,在 WYSegmentViewWidthStyleFixed 中会用到。顺便创建buttons
    CGFloat space = 0;
    CGFloat allTitleSize = 0;
    for (int i = 0; i < _sectionTitles.count; i++) {
        GMButton *button = [GMButton buttonWithType:UIButtonTypeCustom];
        button.tag = i + BUTTON_TAG;
        button.enableAdaptive = YES;
        [button addTarget:self action:@selector(buttonClick:) forControlEvents:UIControlEventTouchUpInside];
        NSString *title = _sectionTitles[i];
        NSAttributedString *attriTitle = [[NSAttributedString alloc] initWithString:title attributes:self.titleTextAttributes];
        NSAttributedString *seletedAttriTitle = [[NSAttributedString alloc] initWithString:title attributes:self.selectedTitleTextAttributes];
        __block CGFloat btnW = [seletedAttriTitle size].width;
        CGFloat btnTitleH = [seletedAttriTitle size].height;
        
        if (self.sectionTitlesInfor != nil &&
            self.sectionTitlesInfor.count > i &&
            [self.sectionTitlesInfor[i] isKindOfClass:[NSDictionary class]] &&
            ((GMSegmentBtnStyle)[(self.sectionTitlesInfor[i][@"btnStyle"]) integerValue]) == GMSegmentBtnStyleLive) {
            NSString *imgurl = self.sectionTitlesInfor[i][@"tabNameImage"];
            if (imgurl && imgurl.length > 0) {
                
                __block CGFloat imgW = 32;
                CGFloat imgH = btnTitleH;
                UIImageView *contentIv = [UIImageView new];
                [button addSubview:contentIv];
                contentIv.tag = 1;
                [contentIv sd_setImageWithURL:[NSURL URLWithString:imgurl] completed:^(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL * _Nullable imageURL) {
                    // 防止多次下载同一张图片,上次下载被取消后返回nil的造成的carsh
                    if (image == nil) return;
                    // 如果该button已经被移除了,就不再进行约束更新操作
                    if (contentIv.superview.superview == nil) return;
                    imgW = ceil((image.size.width * 40)/image.size.height);
                    [contentIv mas_updateConstraints:^(MASConstraintMaker *make) {
                        make.width.mas_equalTo(imgW);
                    }];
                    btnW = imgW;
                }];
                [contentIv mas_makeConstraints:^(MASConstraintMaker *make) {
                    make.width.mas_equalTo(imgW);
                    make.height.mas_equalTo(40);
                    make.center.mas_equalTo(button.center);
                }];
                
                NSString *iconUrl = self.sectionTitlesInfor[i][@"iconUrl"];
                if (iconUrl && iconUrl.length > 0) {
                    
                    UIImageView *liveIv = [UIImageView new];
                    liveIv.tag = 2;
                    __block CGFloat liveIvW = 40;
                    [liveIv sd_setImageWithURL:[NSURL URLWithString:iconUrl] completed:^(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL * _Nullable imageURL) {
                        // 防止多次下载同一张图片,上次下载被取消后返回nil的造成的carsh
                        if (image == nil) return;
                        // 如果该button已经被移除了,就不再进行约束更新操作
                        if (liveIv.superview.superview == nil) return;
                        liveIvW = (image.size.width * 40.0)/image.size.height;
                        [liveIv mas_updateConstraints:^(MASConstraintMaker *make) {
                            make.width.mas_equalTo(liveIvW);
                        }];
                    }];
                    liveIv.clipsToBounds = NO;
                    [button addSubview:liveIv];
                    [liveIv mas_makeConstraints:^(MASConstraintMaker *make) {
                        make.width.mas_equalTo(liveIvW);
                        make.height.mas_equalTo(40);
                        make.left.mas_equalTo(contentIv.mas_right).mas_offset(-7);
                        make.centerY.mas_equalTo(contentIv.centerY);
                    }];
                }
            }else{
                //                如果是Live类型,但是没有图片url,则不显示该btn
                btnW = 0;
                button.alpha = 0;
            }
        }else{
            [button setAttributedTitle:attriTitle forState:UIControlStateNormal];
            
            [button setAttributedTitle:seletedAttriTitle forState:UIControlStateSelected];
        }
        [_container addSubview:button];

        button.width = btnW;
        allTitleSize += button.width;
    }
    // 为buttons添加约束信息
    UIView *preView;
    space = (self.width - allTitleSize - _bothEndPadding * 2) / (_sectionTitles.count + 1); // 计算间距
    for (int i = 0; i < _sectionTitles.count; i++) {
        GMButton *button = _container.subviews[i];
        [button mas_makeConstraints:^(MASConstraintMaker *make) {
            make.top.equalTo(_container).offset(_scrollView.contentInset.top);
            make.bottom.equalTo(_container).offset(-_scrollView.contentInset.bottom);
            make.height.equalTo(_scrollView).offset(-_scrollView.contentInset.top - _scrollView.contentInset.bottom);
            if (self.widthStyle == WYSegmentViewWidthStyleFixed) {  // 之前的全局统一样式:固定样式
                make.width.mas_equalTo(button.width);
                if (i == 0) {
                    make.left.mas_equalTo(space + _bothEndPadding);
                }else {
                    make.left.equalTo(preView.mas_right).offset(space);
                }
                if (i == _sectionTitles.count-1) {
                    make.right.mas_equalTo(-space - _bothEndPadding);
                }
            }else if(self.widthStyle == WYSegmentViewWidthStyleEqual) { // 新添加的全局统一样式:均分样式
                make.width.mas_equalTo(self.width / _sectionTitles.count);
                if (i == 0) {
                    make.left.mas_equalTo(0);
                }else {
                    make.left.equalTo(preView.mas_right);
                }
                if (i == _sectionTitles.count-1) {
                    make.right.mas_equalTo(0);
                }
            }else {  // 动态样式
                // button.width == 0 兼容 非文字的btn并且未获取到图片url的情况
                make.width.mas_equalTo(button.width > 0 ? ceilf(button.width + _itemHorizonalPadding * 2) : 0);
                if(i == 0) {
                    make.left.mas_equalTo(0);
                }else {
                    make.left.equalTo(preView.mas_right);
                }
                if (i == _sectionTitles.count-1) {
                    make.right.mas_equalTo(0);
                }
            }
        }];
        if (self.customButtonUI) {
            self.customButtonUI(button, i);
        }
        preView = button;
    }
}
#pragma mark - Setter
/**
 *	清除原来的 button,并依据 title array 重新添加 button
 *
 *	@param	sectionTitles	待显示的 title array
 */
- (void)setSectionTitles:(NSArray *)sectionTitles
{
    if (sectionTitles.count == 0) {
        return;
    }

    _sectionTitles = sectionTitles;
    [_container.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)];
}

- (void)setShowIndicator:(BOOL)showIndicator {
    _showIndicator = showIndicator;
    _indicatorView.hidden = !_showIndicator;
}

- (void)setIndicatorColor:(UIColor *)indicatorColor {
    _indicatorColor = indicatorColor;
    _indicatorView.backgroundColor = _indicatorColor;
}

- (void)setSectionTitlesInfor:(NSArray *)sectionTitlesInfor
{
    if (sectionTitlesInfor.copy == 0) {
        return;
    }
    _sectionTitlesInfor = sectionTitlesInfor;
}
#pragma mark - Layout Views

- (void)layoutSegments
{
    [self setupSegments];
    [self setSelectedSegmentIndex:_selectedSegmentIndex animated:NO];
}

#pragma mark - Animation

- (void)animateSelectedButtonBackgroundAtIndex:(NSUInteger)index animate:(BOOL)animate
{
    // 将 _indicatorView 移动到相应 button 位置
    UIButton *currentButton = (UIButton *)[_container viewWithTag:index + BUTTON_TAG];

    [UIView animateWithDuration:(animate ? 0.25 : 0.0) animations:^{
        // 改变offset
        // 当rect的中心点大于scrollview的中心点,那就可以把这个button往中间移一下
        if ((_scrollView.contentSize.width > _scrollView.width)) {
            if (CGRectGetMidX(currentButton.frame) > self.width / 2) {

                // 下面这个算法是基于offset为0的情况
                CGFloat offset = CGRectGetMidX(currentButton.frame) - self.width / 2;
                CGFloat maxOffset = _scrollView.contentSize.width - self.width + _scrollView.contentInset.right;
                if (offset > maxOffset) {
                    offset = maxOffset;
                }
                _scrollView.contentOffset = CGPointMake(offset, 0);
            }else {
                _scrollView.contentOffset = CGPointMake(-_scrollView.contentInset.left, 0);
            }
        }
        
        // 改变indicator的坐标
        if (self.updateIndicatorFrame) {
            self.updateIndicatorFrame(_indicatorView, currentButton);
        }
    }];
}


- (void)setSelectedSegmentIndex:(NSInteger)selectedSegmentIndex animated:(BOOL)animated {
    // 通过使用 selectedIndex 的 set 方法来执行button选择状态的 UI 变化,详见其 set 方法
    [self changeStatusForePreButton:self.selectedSegmentIndex newButton:selectedSegmentIndex];
    
    // 改变属性值
    _selectedSegmentIndex = selectedSegmentIndex;
    
    // 调用动画
    [self animateSelectedButtonBackgroundAtIndex:selectedSegmentIndex animate:animated];
}

- (void)changeStatusForePreButton:(NSInteger)preButtonIndex newButton:(NSInteger)newButtonIndex{
    
    // 取消先前 button 的状态
    UIButton *previousbutton = (UIButton *)[_container viewWithTag:preButtonIndex + BUTTON_TAG];
    previousbutton.selected = NO;
    
    // 处理新的 button
    UIButton *currentButton = (UIButton *)[_container viewWithTag:newButtonIndex + BUTTON_TAG];
    currentButton.selected = YES;
    
    // 立即变更button状态,以防之后的动画影响button title的改变
    [self layoutIfNeeded];
}


#pragma mark - Action
- (void)buttonClick:(UIButton *)button
{
    [self setSelectedSegmentIndex:button.tag - BUTTON_TAG animated:YES];
    
    if (_didSelectedBlock) {
        _didSelectedBlock(self.selectedSegmentIndex);
    }
}

@end