美文网首页
自定义UISegmentedControl分段控制器之ABSeg

自定义UISegmentedControl分段控制器之ABSeg

作者: SAW_ | 来源:发表于2018-06-08 17:28 被阅读41次

    https://github.com/luffySaw/ABSegmentView
    如果每个产品设计都能根据苹果的UI风格来的话,那就省事很多了,但是现实是残酷的,而且如果都用一套苹果原生UI的,恩,看起来也很蛋疼。每个app打开都一个鸟UI,恩,也会视觉疲劳。因此就必须自定义各种各样的UI控件来满足产品UI需求。
    今天分享一个自己产品UI需求效果的分段控制器。因为系统的UISegmentedControl局限性,很多效果没法实现,只能自定义,纵观各种app,基本上难看到原生的UISegmentedControl
    先看下效果图



    这个自定义SegmentView除了基本的标题选择、底部滚动条等,还支持小红点提醒。实现也很简单,当然也可以根据业务需求,自己慢慢增加功能。具体看代码:
    #import <UIKit/UIKit.h>
    
    @class ABSegmentStyle;
    
    @interface ABSegmentStyle : NSObject
    
    /** 滚动条的颜色 */
    @property (nonatomic, strong) UIColor *bottomLineColor;
    /** 滚动条的高度 默认为2 */
    @property (assign, nonatomic) CGFloat bottomLineHeight;
    /** 标题的字体 默认为17 */
    @property (strong, nonatomic) UIFont *titleFont;
    /** 标题一般状态的颜色 */
    @property (strong, nonatomic) UIColor *normalTitleColor;
    /** 标题选中状态的颜色 */
    @property (strong, nonatomic) UIColor *selectedTitleColor;
    /** 隐藏底部边框线,默认YES */
    @property (assign, nonatomic) BOOL hiddenBottomBorderLine;
    
    @end
    
    
    typedef void(^TitleBtnOnClickBlock)(NSString *title, NSInteger index);
    
    @interface ABSegmentView : UIView
    
    -(instancetype)initWithFrame:(CGRect)frame segmentStyle:(ABSegmentStyle *)segmentStyle titles:(NSArray<NSString *> *)titles titleDidClick:(TitleBtnOnClickBlock)titleDidClick;
    
    @property (nonatomic, assign, readonly) NSInteger selectedIndex;
    
    /**
     设置选中的下标
    
     @param index 下标
     @param animated 动画
     */
    - (void)setSelectedIndex:(NSInteger)index animated:(BOOL)animated;
    
    /**
     根据外部滚动视图来调整底部横线位置
    
     @param scrollView 外部滚动视图
     */
    - (void)adjustScollViewDidScroll:(UIScrollView *)scrollView;
    
    /**
     显示小红点
    
     @param index 位置
     */
    - (void)showBaggeOnItemIndex:(NSInteger)index;
    
    /**
     隐藏小红点
     
     @param index 位置
     */
    - (void)hideBaggeOnItemIndex:(NSInteger)index;
    
    @end
    
    #import "ABSegmentView.h"
    
    @implementation ABSegmentStyle
    
    - (instancetype)init
    {
        if(self = [super init]) {
            self.bottomLineHeight = 3.0;
            self.bottomLineColor = [UIColor wildtoGreenNormal];
            self.titleFont = [UIFont systemFontOfSize:17.0];
            self.normalTitleColor = [UIColor titleLightGreyColor];
            self.selectedTitleColor = [UIColor wildtoGreenNormal];
            self.hiddenBottomBorderLine = YES;
        }
        return self;
    }
    
    @end
    
    
    @interface ABSegmentView ()
    {
        NSUInteger _currentIndex;
        NSUInteger _oldIndex;
    }
    @property (nonatomic, strong) ABSegmentStyle *segmentStyle;
    @property (nonatomic, weak) UIView *bottomLine;
    @property (nonatomic, weak) UIView *moveClearView;
    @property (nonatomic, strong) NSArray *titles;
    @property (nonatomic, strong) NSMutableArray *titleViews;
    @property (nonatomic, strong) NSMutableArray *titleLabelRects;
    @property (nonatomic, copy) TitleBtnOnClickBlock titleBtnOnClick;
    @property (nonatomic, weak) UIView *viewBottomBorderLine;
    
    @end
    
    @implementation ABSegmentView
    
    - (UIView *)bottomLine
    {
        if (!_bottomLine) {
            UIView *lineView = [[UIView alloc] init];
            lineView.backgroundColor = self.segmentStyle.bottomLineColor;
            [self.moveClearView addSubview:lineView];
            _bottomLine = lineView;
        }
        return _bottomLine;
    }
    
    -(UIView *)moveClearView
    {
        if (!_moveClearView) {
            UIView *moveClearView = [[UIView alloc] init];
            moveClearView.backgroundColor = [UIColor clearColor];
            [self addSubview:moveClearView];
            _moveClearView = moveClearView;
        }
        return _moveClearView;
    }
    
    -(NSMutableArray *)titleViews
    {
        if (!_titleViews) {
            _titleViews = [NSMutableArray array];
        }
        return _titleViews;
    }
    
    -(NSMutableArray *)titleLabelRects
    {
        if (!_titleLabelRects) {
            _titleLabelRects = [NSMutableArray array];
        }
        return _titleLabelRects;
    }
    
    -(instancetype)initWithFrame:(CGRect)frame segmentStyle:(ABSegmentStyle *)segmentStyle titles:(NSArray<NSString *> *)titles titleDidClick:(TitleBtnOnClickBlock)titleDidClick
    {
        if (self = [super initWithFrame:frame]) {
            self.titles = titles;
            self.titleBtnOnClick = titleDidClick;
            self.segmentStyle = segmentStyle;
            _currentIndex = 0;
            _oldIndex = 0;
            
            [self setuptitleViews];
            [self setupUI];
        }
        return self;
    }
    
    -(void)layoutSubviews
    {
        [super layoutSubviews];
    }
    
    - (void)setuptitleViews
    {
        NSInteger index = 0;
        for (NSString *title in self.titles) {
            
            UIView *titleView = [[UIView alloc]initWithFrame:CGRectZero];
            titleView.tag = index;
            titleView.userInteractionEnabled = YES;
            UITapGestureRecognizer *tapGes = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(titleViewOnClick:)];
            [titleView addGestureRecognizer:tapGes];
            titleView.backgroundColor = [UIColor clearColor];
            [self addSubview:titleView];
            
            //标题
            UILabel *label = [[UILabel alloc] initWithFrame:CGRectZero];
            label.tag = index;
            label.text = title;
            label.textColor = self.segmentStyle.normalTitleColor;
            label.font = self.segmentStyle.titleFont;
            [titleView addSubview:label];
            
            //红点提醒
            UIView *redPointView = [[UIView alloc]initWithFrame:CGRectZero];
            redPointView.backgroundColor = ColorWithHex(@"ff3b30");
            redPointView.layer.cornerRadius = 4;
            redPointView.layer.masksToBounds = YES;
            redPointView.tag = 250;
            redPointView.hidden = YES;
            [titleView addSubview:redPointView];
            
            [self.titleViews addObject:titleView];
            
            CGRect bounds = [title boundingRectWithSize:CGSizeMake(MAXFLOAT, 0.0) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName:self.segmentStyle.titleFont} context:nil];
            [self.titleLabelRects addObject:[NSValue valueWithCGRect:bounds]];
            
            index++;
        }
    }
    
    - (void)setupUI
    {
        [self setupTtitleLabelFrame];
        [self setupBottomLineFrame];
        [self setupViewBottomBorderLine];
    }
    
    - (void)setupTtitleLabelFrame
    {
        CGFloat labelX = 0;
        CGFloat labelY = 0;
        CGFloat labelW = self.frame.size.width / self.titleViews.count;
        CGFloat labelH = self.frame.size.height;
        
        NSInteger index = 0;
        
        for (UIView *titleView in self.titleViews) {
            labelX = index * labelW;
            titleView.frame = CGRectMake(labelX, labelY, labelW, labelH);
            
            for (UIView *subView in titleView.subviews) {
                if ([subView isKindOfClass:[UILabel class]]) {
                    UILabel *subLabel = (UILabel *)subView;
                    if (subLabel.tag == titleView.tag) {
                        CGRect labBounds = [self.titleLabelRects[index] CGRectValue];
                        CGFloat subLabelW = labBounds.size.width;
                        CGFloat subLabelH = labBounds.size.height;
                        CGFloat subLabelX = (titleView.ab_width - subLabelW) / 2;
                        CGFloat subLabelY = (titleView.ab_height - subLabelH) / 2;
                        subLabel.frame = CGRectMake(subLabelX, subLabelY, subLabelW, subLabelH);
                        [self solveUIWidgetFuzzy:subLabel];
                        if (index == 0) {
                            subLabel.textColor = self.segmentStyle.selectedTitleColor;
                        }
                        
                        UIView *redPointView = [titleView viewWithTag:250];
                        if (redPointView) {
                            CGFloat redPointW = 8;
                            CGFloat redPointH = 8;
                            CGFloat redPointX = CGRectGetMaxX(subLabel.frame);
                            CGFloat redPointY = subLabel.ab_y - redPointH / 2;
                            redPointView.frame = CGRectMake(redPointX, redPointY, redPointW, redPointH);
                        }
                    }
                }
                continue;
            }
            
            index++;
        }
    }
    
    - (void)solveUIWidgetFuzzy:(UIView *)view
    {
        CGRect frame = view.frame;
        int x = floor(frame.origin.x);
        int y = floor(frame.origin.y);
        int w = floor(frame.size.width) + 1;
        int h = floor(frame.size.height) + 1;
        
        view.frame = CGRectMake(x, y, w, h);
    }
    
    - (void)setupBottomLineFrame
    {
        UIView *firstTitleView = [self.titleViews firstObject];
        
        if (self.moveClearView) {
            self.moveClearView.ab_y = 0;
            self.moveClearView.ab_width = firstTitleView.frame.size.width;
            self.moveClearView.ab_height = firstTitleView.frame.size.height;
            self.moveClearView.ab_x = firstTitleView.frame.origin.x;
        }
        
        CGRect titleLabRect = [[self.titleLabelRects firstObject] CGRectValue];
        CGFloat lineW = titleLabRect.size.width;
        CGFloat lineH = self.segmentStyle.bottomLineHeight;
        CGFloat lineY = self.moveClearView.frame.size.height - lineH;
        
        if (self.bottomLine) {
            self.bottomLine.ab_x = (self.moveClearView.frame.size.width - lineW) / 2;
            self.bottomLine.ab_y = lineY;
            self.bottomLine.ab_width = lineW;
            self.bottomLine.ab_height = lineH;
        }
    }
    
    -(void)setupViewBottomBorderLine
    {
        CGFloat lineH = [GlobalPublicMethods singleLineAdjustOffset];
        UIView *viewBottomBorderLine = [[UIView alloc]initWithFrame:CGRectMake(0, self.ab_height - lineH, self.ab_width, lineH)];
        viewBottomBorderLine.backgroundColor = [UIColor separatorColor];
        [self addSubview:viewBottomBorderLine];
        self.viewBottomBorderLine = viewBottomBorderLine;
        self.viewBottomBorderLine.hidden = self.segmentStyle.hiddenBottomBorderLine;
    }
    
    #pragma mark - titleViewOnClick
    - (void)titleViewOnClick:(UITapGestureRecognizer *)tapGes
    {
        UIView *currentTitleView = tapGes.view;
        if (!currentTitleView) {
            return;
        }
        _currentIndex = currentTitleView.tag;
        
        [self adjustUIWhenBtnOnClickWithAnimated:YES];
    }
    
    - (void)adjustUIWhenBtnOnClickWithAnimated:(BOOL)animated
    {
        if (_currentIndex == _oldIndex) {
            return;
        }
    
        CGFloat animatedTime = animated ? 0.2 : 0.0;
        
        CGRect titleLabelRect = [self.titleLabelRects[_currentIndex] CGRectValue];
        CGFloat currentBottomLinW = titleLabelRect.size.width;
        
        UIView *oldTitleView = self.titleViews[_oldIndex];
        UILabel *oldTitleLabel = nil;
        for (UIView *subView in oldTitleView.subviews) {
            if ([subView isKindOfClass:[UILabel class]]) {
                UILabel *subLabel = (UILabel *)subView;
                if (subLabel.tag == oldTitleView.tag) {
                    oldTitleLabel = subLabel;
                }
            }
        }
        UIView *currentTitleView = self.titleViews[_currentIndex];
        UILabel *currentTitleLabel = nil;
        for (UIView *subView in currentTitleView.subviews) {
            if ([subView isKindOfClass:[UILabel class]]) {
                UILabel *subLabel = (UILabel *)subView;
                if (subLabel.tag == currentTitleView.tag) {
                    currentTitleLabel = subLabel;
                }
            }
        }
        
        __weak typeof(self) weakSelf = self;
        [UIView animateWithDuration:animatedTime animations:^{
            oldTitleLabel.textColor = weakSelf.segmentStyle.normalTitleColor;
            currentTitleLabel.textColor = weakSelf.segmentStyle.selectedTitleColor;
            
            weakSelf.moveClearView.ab_x = currentTitleView.ab_x;
            
            weakSelf.bottomLine.ab_width = currentBottomLinW;
            weakSelf.bottomLine.ab_x = (weakSelf.moveClearView.ab_width - currentBottomLinW) / 2;
        }];
        
        _oldIndex = _currentIndex;
        
        _selectedIndex = _currentIndex;
        
        if (self.titleBtnOnClick) {
            self.titleBtnOnClick(currentTitleLabel.text,_currentIndex);
        }
    }
    
    - (void)setSelectedIndex:(NSInteger)index animated:(BOOL)animated
    {
        if (index < 0 || index >= self.titles.count) {
            return;
        }
        _currentIndex = index;
        [self adjustUIWhenBtnOnClickWithAnimated:animated];
    }
    
    -(void)adjustScollViewDidScroll:(UIScrollView *)scrollView
    {
        CGFloat pageWidth = scrollView.frame.size.width;
        CGFloat offsetX = scrollView.contentOffset.x;
        CGFloat changeX = offsetX / (pageWidth / self.frame.size.width) / self.titles.count;
        self.moveClearView.ab_x = changeX;
    }
    
    -(void)showBaggeOnItemIndex:(NSInteger)index
    {
        if (index < 0 || index > self.titleViews.count) {
            return;
        }
        
        UIView *titleView = self.titleViews[index];
        UIView *redPointView = [titleView viewWithTag:250];
        if (redPointView) {
            if (redPointView.hidden) {
                redPointView.hidden = NO;
            }
        }
    }
    
    - (void)hideBaggeOnItemIndex:(NSInteger)index
    {
        if (index < 0 || index > self.titleViews.count) {
            return;
        }
        
        UIView *titleView = self.titleViews[index];
        UIView *redPointView = [titleView viewWithTag:250];
        if (redPointView) {
            if (!redPointView.hidden) {
                redPointView.hidden = YES;
            }
        }
    }
    
    @end
    

    使用初始化也很简单:

        ABSegmentStyle *style = [[ABSegmentStyle alloc]init];
        style.titleFont = [UIFont boldSystemFontOfSize:17];
    
        YTWeakSelf(self);
        ABSegmentView *segmentView = [[ABSegmentView alloc]initWithFrame:CGRectMake(0, 0, kWidthValue(225), 44) segmentStyle:style titles:@[YTLocalizedString(@"跑步"),YTLocalizedString(@"自行车"),YTLocalizedString(@"铁三")] titleDidClick:^(NSString *title, NSInteger index) {
            if (weakself.scrollview) {
                [weakself.scrollview setContentOffset:CGPointMake(weakself.scrollview.frame.size.width * index, 0) animated:NO];
                [weakself scrollViewDidEndScrollingAnimation:weakself.scrollview];
                weakself.scrollview.scrollEnabled = YES;
            }
        }];
        self.navigationItem.titleView = segmentView;
        self.segmentView = segmentView;
        [segmentView setSelectedIndex:1 animated:NO];
    
    - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
    {
        CGFloat pageWidth = scrollView.frame.size.width;
        NSInteger page = scrollView.contentOffset.x / pageWidth;
        [self scrollViewDidEndScrollingAnimation:scrollView];
        
        [self.segmentView setSelectedIndex:page animated:YES];
    }
    
    -(void)scrollViewDidScroll:(UIScrollView *)scrollView
    {
        [self.segmentView adjustScollViewDidScroll:scrollView];
    }
    

    相关文章

      网友评论

          本文标题:自定义UISegmentedControl分段控制器之ABSeg

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