// // WYSegmentView.h // GengMei // // Created by wangyang on 13-8-9. // Copyright (c) 2015 Gengmei. All rights reserved. // #import "WYSegmentView.h" #import #import #import "UIView+Layout.h" #import #import "UIView+LineWithAutolayout.h" #import "GMFont.h" #import #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