美文网首页OC-框架iOS Developer
HMSegmentedControl源代码解析

HMSegmentedControl源代码解析

作者: linbj | 来源:发表于2017-04-12 11:51 被阅读506次

    HMSegmentedControl地址
    HMSegmentedControl是一款选项卡的三方框架,github上有3000星左右。
    研究了一下源码,并添加了一点注释。

    HMSegmentedControl基于UIControl,上面有一个UIScrollView。
    文字,图片,指示器都是用CALayer,UIBezierPath处理创建的。

    //
    //  HMSegmentedControl.h
    //  HMSegmentedControl
    //
    //  Created by Hesham Abd-Elmegid on 23/12/12.
    //  Copyright (c) 2012-2015 Hesham Abd-Elmegid. All rights reserved.
    //
    
    #import <UIKit/UIKit.h>
    
    @class HMSegmentedControl;
    
    //点击按钮的block
    typedef void (^IndexChangeBlock)(NSInteger index);
    
    //Title富文本
    typedef NSAttributedString *(^HMTitleFormatterBlock)(HMSegmentedControl *segmentedControl, NSString *title, NSUInteger index, BOOL selected);
    
    //指示器的样式
    typedef NS_ENUM(NSInteger, HMSegmentedControlSelectionStyle) {
        //指示器和文字同宽
        HMSegmentedControlSelectionStyleTextWidthStripe, // Indicator width will only be as big as the text width
        //指示器和segment同宽
        HMSegmentedControlSelectionStyleFullWidthStripe, // Indicator width will fill the whole segment
        //矩形指示器
        HMSegmentedControlSelectionStyleBox, // A rectangle that covers the whole segment
        //箭头指示器
        HMSegmentedControlSelectionStyleArrow // An arrow in the middle of the segment pointing up or down depending on `HMSegmentedControlSelectionIndicatorLocation`
    };
    
    //指示器位置样式
    typedef NS_ENUM(NSInteger, HMSegmentedControlSelectionIndicatorLocation) {
        //上
        HMSegmentedControlSelectionIndicatorLocationUp,
        //下
        HMSegmentedControlSelectionIndicatorLocationDown,
        //无
        HMSegmentedControlSelectionIndicatorLocationNone // No selection indicator
    };
    
    typedef NS_ENUM(NSInteger, HMSegmentedControlSegmentWidthStyle) {
        //宽度fix
        HMSegmentedControlSegmentWidthStyleFixed, // Segment width is fixed
        //segment的宽度和text的宽度一致
        HMSegmentedControlSegmentWidthStyleDynamic, // Segment width will only be as big as the text width (including inset)
    };
    
    //边框类型
    typedef NS_OPTIONS(NSInteger, HMSegmentedControlBorderType) {
        HMSegmentedControlBorderTypeNone = 0,
        HMSegmentedControlBorderTypeTop = (1 << 0),
        HMSegmentedControlBorderTypeLeft = (1 << 1),
        HMSegmentedControlBorderTypeBottom = (1 << 2),
        HMSegmentedControlBorderTypeRight = (1 << 3)
    };
    
    enum {
        HMSegmentedControlNoSegment = -1   // Segment index for no selected segment
    };
    
    //segment类型
    typedef NS_ENUM(NSInteger, HMSegmentedControlType) {
        //文本
        HMSegmentedControlTypeText,
        //图片
        HMSegmentedControlTypeImages,
        //图文
        HMSegmentedControlTypeTextImages
    };
    
    @interface HMSegmentedControl : UIControl
    
    @property (nonatomic, strong) NSArray<NSString *> *sectionTitles; // section标题
    @property (nonatomic, strong) NSArray<UIImage *> *sectionImages;  // section图片
    @property (nonatomic, strong) NSArray<UIImage *> *sectionSelectedImages; // 选中的section图片
    
    /**
     Provide a block to be executed when selected index is changed.
     
     Alternativly, you could use `addTarget:action:forControlEvents:`
     */
    
    /**
     选中index之后的回调
     可以使用`addTarget:action:forControlEvents:`替代
     */
    @property (nonatomic, copy) IndexChangeBlock indexChangeBlock;
    
    /**
     Used to apply custom text styling to titles when set.
     
     When this block is set, no additional styling is applied to the `NSAttributedString` object returned from this block.
     */
    
    /**
     自定义富文本标题
     */
    @property (nonatomic, copy) HMTitleFormatterBlock titleFormatter;
    
    /**
     Text attributes to apply to item title text.
     */
    
    /**
     标题 text 属性
     */
    @property (nonatomic, strong) NSDictionary *titleTextAttributes UI_APPEARANCE_SELECTOR;
    
    /*
     Text attributes to apply to selected item title text.
     
     Attributes not set in this dictionary are inherited from `titleTextAttributes`.
     */
    @property (nonatomic, strong) NSDictionary *selectedTitleTextAttributes UI_APPEARANCE_SELECTOR;
    
    /**
     Segmented control background color.
     
     Default is `[UIColor whiteColor]`
     */
    
    /**
     背景颜色
     */
    @property (nonatomic, strong) UIColor *backgroundColor UI_APPEARANCE_SELECTOR;
    
    /**
     Color for the selection indicator stripe
     
     Default is `R:52, G:181, B:229`
     */
    /**
     指示器颜色
     */
    @property (nonatomic, strong) UIColor *selectionIndicatorColor UI_APPEARANCE_SELECTOR;
    
    /**
     Color for the selection indicator box
     
     Default is selectionIndicatorColor
     */
    
    /**
     box颜色
     */
    @property (nonatomic, strong) UIColor *selectionIndicatorBoxColor UI_APPEARANCE_SELECTOR;
    
    /**
     Color for the vertical divider between segments.
     
     Default is `[UIColor blackColor]`
     */
    
    /**
     segment间的分割线的颜色
     */
    @property (nonatomic, strong) UIColor *verticalDividerColor UI_APPEARANCE_SELECTOR;
    
    /**
     Opacity for the seletion indicator box.
     
     Default is `0.2f`
     */
    /**
     不透明的盒子对比指标
     */
    @property (nonatomic) CGFloat selectionIndicatorBoxOpacity;
    
    /**
     Width the vertical divider between segments that is added when `verticalDividerEnabled` is set to YES.
     
     Default is `1.0f`
     */
    
    /**
     分割线高度
     */
    @property (nonatomic, assign) CGFloat verticalDividerWidth;
    
    /**
     Specifies the style of the control
     
     Default is `HMSegmentedControlTypeText`
     */
    @property (nonatomic, assign) HMSegmentedControlType type;
    
    /**
     Specifies the style of the selection indicator.
     
     Default is `HMSegmentedControlSelectionStyleTextWidthStripe`
     */
    @property (nonatomic, assign) HMSegmentedControlSelectionStyle selectionStyle;
    
    /**
     Specifies the style of the segment's width.
     
     Default is `HMSegmentedControlSegmentWidthStyleFixed`
     */
    @property (nonatomic, assign) HMSegmentedControlSegmentWidthStyle segmentWidthStyle;
    
    /**
     Specifies the location of the selection indicator.
     
     Default is `HMSegmentedControlSelectionIndicatorLocationUp`
     */
    @property (nonatomic, assign) HMSegmentedControlSelectionIndicatorLocation selectionIndicatorLocation;
    
    /*
     Specifies the border type.
     
     Default is `HMSegmentedControlBorderTypeNone`
     */
    @property (nonatomic, assign) HMSegmentedControlBorderType borderType;
    
    /**
     Specifies the border color.
     
     Default is `[UIColor blackColor]`
     */
    @property (nonatomic, strong) UIColor *borderColor;
    
    /**
     Specifies the border width.
     
     Default is `1.0f`
     */
    
    /**
     边框颜色
     */
    @property (nonatomic, assign) CGFloat borderWidth;
    
    /**
     Default is YES. Set to NO to deny scrolling by dragging the scrollView by the user.
     */
    
    /**
     是否可以拖拽
     */
    @property(nonatomic, getter = isUserDraggable) BOOL userDraggable;
    
    /**
     Default is YES. Set to NO to deny any touch events by the user.
     */
    
    /**
     是否可以点击
     */
    @property(nonatomic, getter = isTouchEnabled) BOOL touchEnabled;
    
    /**
     Default is NO. Set to YES to show a vertical divider between the segments.
     */
    
    /**
     分割线是否显示
     */
    @property(nonatomic, getter = isVerticalDividerEnabled) BOOL verticalDividerEnabled;
    
    /**
     Index of the currently selected segment.
     */
    
    /**
     选中的标号
     */
    @property (nonatomic, assign) NSInteger selectedSegmentIndex;
    
    /**
     Height of the selection indicator. Only effective when `HMSegmentedControlSelectionStyle` is either `HMSegmentedControlSelectionStyleTextWidthStripe` or `HMSegmentedControlSelectionStyleFullWidthStripe`.
     
     Default is 5.0
     */
    
    /**
     指示器高度
     */
    @property (nonatomic, readwrite) CGFloat selectionIndicatorHeight;
    
    /**
     Edge insets for the selection indicator.
     NOTE: This does not affect the bounding box of HMSegmentedControlSelectionStyleBox
     
     When HMSegmentedControlSelectionIndicatorLocationUp is selected, bottom edge insets are not used
     
     When HMSegmentedControlSelectionIndicatorLocationDown is selected, top edge insets are not used
     
     Defaults are top: 0.0f
                 left: 0.0f
               bottom: 0.0f
                right: 0.0f
     */
    @property (nonatomic, readwrite) UIEdgeInsets selectionIndicatorEdgeInsets;
    
    /**
     Inset left and right edges of segments.
     
     Default is UIEdgeInsetsMake(0, 5, 0, 5)
     */
    @property (nonatomic, readwrite) UIEdgeInsets segmentEdgeInset;
    
    @property (nonatomic, readwrite) UIEdgeInsets enlargeEdgeInset;
    
    /**
     Default is YES. Set to NO to disable animation during user selection.
     */
    @property (nonatomic) BOOL shouldAnimateUserSelection;
    
    //初始化
    - (id)initWithSectionTitles:(NSArray<NSString *> *)sectiontitles;
    - (id)initWithSectionImages:(NSArray<UIImage *> *)sectionImages sectionSelectedImages:(NSArray<UIImage *> *)sectionSelectedImages;
    - (instancetype)initWithSectionImages:(NSArray<UIImage *> *)sectionImages sectionSelectedImages:(NSArray<UIImage *> *)sectionSelectedImages titlesForSections:(NSArray<NSString *> *)sectiontitles;
    - (void)setSelectedSegmentIndex:(NSUInteger)index animated:(BOOL)animated;
    - (void)setIndexChangeBlock:(IndexChangeBlock)indexChangeBlock;
    - (void)setTitleFormatter:(HMTitleFormatterBlock)titleFormatter;
    
    @end
    
    
    segment
    //
    //  HMSegmentedControl.m
    //  HMSegmentedControl
    //
    //  Created by Hesham Abd-Elmegid on 23/12/12.
    //  Copyright (c) 2012-2015 Hesham Abd-Elmegid. All rights reserved.
    //
    
    #import "HMSegmentedControl.h"
    #import <QuartzCore/QuartzCore.h>
    #import <math.h>
    
    @interface HMScrollView : UIScrollView
    @end
    
    
    @implementation HMScrollView
    
    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
        if (!self.dragging) {
            [self.nextResponder touchesBegan:touches withEvent:event];
        } else {
            [super touchesBegan:touches withEvent:event];
        }
    }
    
    - (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
        if (!self.dragging) {
            [self.nextResponder touchesMoved:touches withEvent:event];
        } else{
            [super touchesMoved:touches withEvent:event];
        }
    }
    
    - (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
        if (!self.dragging) {
            [self.nextResponder touchesEnded:touches withEvent:event];
        } else {
            [super touchesEnded:touches withEvent:event];
        }
    }
    
    @end
    
    
    
    @interface HMSegmentedControl ()
    
    //3中不同的指示器的layer
    @property (nonatomic, strong) CALayer *selectionIndicatorStripLayer;
    @property (nonatomic, strong) CALayer *selectionIndicatorBoxLayer;
    @property (nonatomic, strong) CALayer *selectionIndicatorArrowLayer;
    
    @property (nonatomic, readwrite) CGFloat segmentWidth;
    @property (nonatomic, readwrite) NSArray<NSNumber *> *segmentWidthsArray;
    @property (nonatomic, strong) HMScrollView *scrollView;
    
    @end
    
    @implementation HMSegmentedControl
    
    #pragma mark - 初始化
    - (id)initWithCoder:(NSCoder *)aDecoder {
        self = [super initWithCoder:aDecoder];
        if (self) {
            [self commonInit];
        }
        return self;
    }
    
    - (id)initWithFrame:(CGRect)frame {
        self = [super initWithFrame:frame];
        
        if (self) {
            [self commonInit];
        }
        
        return self;
    }
    
    - (id)initWithSectionTitles:(NSArray<NSString *> *)sectiontitles {
        self = [super initWithFrame:CGRectZero];
        
        if (self) {
            [self commonInit];
            self.sectionTitles = sectiontitles;
            self.type = HMSegmentedControlTypeText;
        }
        
        return self;
    }
    
    - (id)initWithSectionImages:(NSArray<UIImage *> *)sectionImages sectionSelectedImages:(NSArray<UIImage *> *)sectionSelectedImages {
        self = [super initWithFrame:CGRectZero];
        
        if (self) {
            [self commonInit];
            self.sectionImages = sectionImages;
            self.sectionSelectedImages = sectionSelectedImages;
            self.type = HMSegmentedControlTypeImages;
        }
        
        return self;
    }
    
    - (instancetype)initWithSectionImages:(NSArray<UIImage *> *)sectionImages sectionSelectedImages:(NSArray<UIImage *> *)sectionSelectedImages titlesForSections:(NSArray<NSString *> *)sectiontitles {
        self = [super initWithFrame:CGRectZero];
        
        if (self) {
            [self commonInit];
            
            if (sectionImages.count != sectiontitles.count) {
                //报错信息
                [NSException raise:NSRangeException format:@"***%s: Images bounds (%ld) Don't match Title bounds (%ld)", sel_getName(_cmd), (unsigned long)sectionImages.count, (unsigned long)sectiontitles.count];
            }
            
            self.sectionImages = sectionImages;
            self.sectionSelectedImages = sectionSelectedImages;
            self.sectionTitles = sectiontitles;
            self.type = HMSegmentedControlTypeTextImages;
        }
        
        return self;
    }
    
    - (void)awakeFromNib {
        [super awakeFromNib];
        
        self.segmentWidth = 0.0f;
    }
    
    #pragma mark - createView
    
    /**
     初始化界面与参数
     */
    - (void)commonInit {
        //创建底层scrollview
        self.scrollView = [[HMScrollView alloc] init];
        self.scrollView.scrollsToTop = NO;
        self.scrollView.showsVerticalScrollIndicator = NO;
        self.scrollView.showsHorizontalScrollIndicator = NO;
        [self addSubview:self.scrollView];
        
        _backgroundColor = [UIColor whiteColor];
        
        //opaque也是表示当前的UIView的不透明度,设置是否之后对于UIView的显示并没有什么影响,官方文档的意思简单点说就是opaque默认为YES,如果alpha小于1,那么应该设置opaque设置为NO,但是如果alpha为1,opaque设置为NO,产生的后果是不可预料的~
        self.opaque = NO;
        
        //设置指示器背景颜色
        _selectionIndicatorColor = [UIColor colorWithRed:52.0f/255.0f green:181.0f/255.0f blue:229.0f/255.0f alpha:1.0f];
        _selectionIndicatorBoxColor = _selectionIndicatorColor;
    
        //默认选中0
        self.selectedSegmentIndex = 0;
        self.segmentEdgeInset = UIEdgeInsetsMake(0, 5, 0, 5);
        self.selectionIndicatorHeight = 5.0f;
        self.selectionIndicatorEdgeInsets = UIEdgeInsetsMake(0.0f, 0.0f, 0.0f, 0.0f);
        self.selectionStyle = HMSegmentedControlSelectionStyleTextWidthStripe;
        self.selectionIndicatorLocation = HMSegmentedControlSelectionIndicatorLocationUp;
        self.segmentWidthStyle = HMSegmentedControlSegmentWidthStyleFixed;
        self.userDraggable = YES;
        self.touchEnabled = YES;
        self.verticalDividerEnabled = NO;
        self.type = HMSegmentedControlTypeText;
        self.verticalDividerWidth = 1.0f;
        _verticalDividerColor = [UIColor blackColor];
        self.borderColor = [UIColor blackColor];
        self.borderWidth = 1.0f;
        
        self.shouldAnimateUserSelection = YES;
        
        //初始化三个layer
        self.selectionIndicatorArrowLayer = [CALayer layer];
        self.selectionIndicatorStripLayer = [CALayer layer];
        self.selectionIndicatorBoxLayer   = [CALayer layer];
        self.selectionIndicatorBoxLayer.opacity = self.selectionIndicatorBoxOpacity;
        self.selectionIndicatorBoxLayer.borderWidth = 1.0f;
        self.selectionIndicatorBoxOpacity = 0.2;
        
        // redraw on bounds change (calls -setNeedsDisplay)
        // 当界面的bounds改变的时候调用-setNeedsDisplay
        self.contentMode = UIViewContentModeRedraw;
    }
    
    - (void)layoutSubviews {
        [super layoutSubviews];
        
        [self updateSegmentsRects];
    }
    

    getter & setter

    
    - (void)setFrame:(CGRect)frame {
        [super setFrame:frame];
        
        [self updateSegmentsRects];
    }
    
    - (void)setSectionTitles:(NSArray<NSString *> *)sectionTitles {
        _sectionTitles = sectionTitles;
        
        // Allows you to perform layout before the drawing cycle happens. -layoutIfNeeded forces layout early
        [self setNeedsLayout];
        [self setNeedsDisplay];
    }
    
    - (void)setSectionImages:(NSArray<UIImage *> *)sectionImages {
        _sectionImages = sectionImages;
        
        [self setNeedsLayout];
        [self setNeedsDisplay];
    }
    
    - (void)setSelectionIndicatorLocation:(HMSegmentedControlSelectionIndicatorLocation)selectionIndicatorLocation {
        _selectionIndicatorLocation = selectionIndicatorLocation;
        
        if (selectionIndicatorLocation == HMSegmentedControlSelectionIndicatorLocationNone) {
            self.selectionIndicatorHeight = 0.0f;
        }
    }
    
    - (void)setSelectionIndicatorBoxOpacity:(CGFloat)selectionIndicatorBoxOpacity {
        _selectionIndicatorBoxOpacity = selectionIndicatorBoxOpacity;
        
        self.selectionIndicatorBoxLayer.opacity = _selectionIndicatorBoxOpacity;
    }
    
    - (void)setSegmentWidthStyle:(HMSegmentedControlSegmentWidthStyle)segmentWidthStyle {
        // Force HMSegmentedControlSegmentWidthStyleFixed when type is HMSegmentedControlTypeImages.
        if (self.type == HMSegmentedControlTypeImages) {
            _segmentWidthStyle = HMSegmentedControlSegmentWidthStyleFixed;
        } else {
            _segmentWidthStyle = segmentWidthStyle;
        }
    }
    
    - (void)setBorderType:(HMSegmentedControlBorderType)borderType {
        _borderType = borderType;
        [self setNeedsDisplay];
    }
    
    UIView的setNeedsDisplay和setNeedsLayout方法
    
    两个方法都是异步执行的。而setNeedsDisplay会调用自动调用drawRect方法,这样可以拿到  UIGraphicsGetCurrentContext,就可以画画了。而setNeedsLayout会默认调用layoutSubViews,
     就可以  处理子视图中的一些数据。
    综上所诉,setNeedsDisplay方便绘图,而layoutSubViews方便出来数据。
    
    layoutSubviews在以下情况下会被调用:
    
    1、init初始化不会触发layoutSubviews。
    2、addSubview会触发layoutSubviews。
    3、设置view的Frame会触发layoutSubviews,当然前提是frame的值设置前后发生了变化。
    4、滚动一个UIScrollView会触发layoutSubviews。
    5、旋转Screen会触发父UIView上的layoutSubviews事件。
    6、改变一个UIView大小的时候也会触发父UIView上的layoutSubviews事件。
    7、直接调用setLayoutSubviews。
    
    drawRect在以下情况下会被调用:
    
    1、如果在UIView初始化时没有设置rect大小,将直接导致drawRect不被自动调用。drawRect调用是在Controller->loadView, Controller->viewDidLoad 两方法之后掉用的.所以不用担心在控制器中,这些View的drawRect就开始画了.这样可以在控制器中设置一些值给View(如果这些View draw的时候需要用到某些变量值).
    2、该方法在调用sizeToFit后被调用,所以可以先调用sizeToFit计算出size。然后系统自动调用drawRect:方法。
    3、通过设置contentMode属性值为UIViewContentModeRedraw。那么将在每次设置或更改frame的时候自动调用drawRect:。
    4、直接调用setNeedsDisplay,或者setNeedsDisplayInRect:触发drawRect:,但是有个前提条件是rect不能为0。
    以上1,2推荐;而3,4不提倡
    
    - (void)drawRect:(CGRect)rect {
        // 设置背景填充色
        [self.backgroundColor setFill];
        UIRectFill([self bounds]);
        
        // 箭头layer指示器的颜色
        self.selectionIndicatorArrowLayer.backgroundColor = self.selectionIndicatorColor.CGColor;
        
        self.selectionIndicatorStripLayer.backgroundColor = self.selectionIndicatorColor.CGColor;
        
        // boxlayer背景颜色与线框颜色
        self.selectionIndicatorBoxLayer.backgroundColor = self.selectionIndicatorBoxColor.CGColor;
        self.selectionIndicatorBoxLayer.borderColor = self.selectionIndicatorBoxColor.CGColor;
        
        // Remove all sublayers to avoid drawing images over existing ones
        // 移除scrollview的所有的layer防止重绘
        self.scrollView.layer.sublayers = nil;
        
        CGRect oldRect = rect;
        
        if (self.type == HMSegmentedControlTypeText) {
            //当segment的type 等于文字的时候
            [self.sectionTitles enumerateObjectsUsingBlock:^(id titleString, NSUInteger idx, BOOL *stop) {
                // 初始化文字高度宽度
                CGFloat stringWidth = 0;
                CGFloat stringHeight = 0;
                
                // 计算文字在有无富文本的高度
                CGSize size = [self measureTitleAtIndex:idx];
                stringWidth = size.width;
                stringHeight = size.height;
                CGRect rectDiv = CGRectZero;
                CGRect fullRect = CGRectZero;
                
                
                // Text inside the CATextLayer will appear blurry unless the rect values are rounded
                // 判断指示器位置是否在上方
                BOOL locationUp = (self.selectionIndicatorLocation == HMSegmentedControlSelectionIndicatorLocationUp);
                // 判断指示器是不是box类型
                BOOL selectionStyleNotBox = (self.selectionStyle != HMSegmentedControlSelectionStyleBox);
    
                // y =(frame的高度 - 是否是box类型 * 指示器的高度) / 2 - 文字高度 / 2 + 指示器的高度 * 是否在上方
                CGFloat y = roundf((CGRectGetHeight(self.frame) - selectionStyleNotBox * self.selectionIndicatorHeight) / 2 - stringHeight / 2 + self.selectionIndicatorHeight * locationUp);
                
                CGRect rect;
                if (self.segmentWidthStyle == HMSegmentedControlSegmentWidthStyleFixed) {
                    // 当width 的类型是 fixed的时候
                    rect = CGRectMake((self.segmentWidth * idx) + (self.segmentWidth - stringWidth) / 2, y, stringWidth, stringHeight);
                    rectDiv = CGRectMake((self.segmentWidth * idx) - (self.verticalDividerWidth / 2), self.selectionIndicatorHeight * 2, self.verticalDividerWidth, self.frame.size.height - (self.selectionIndicatorHeight * 4));
                    fullRect = CGRectMake(self.segmentWidth * idx, 0, self.segmentWidth, oldRect.size.height);
                } else {
                    // When we are drawing dynamic widths, we need to loop the widths array to calculate the xOffset
                    CGFloat xOffset = 0;
                    NSInteger i = 0;
                    for (NSNumber *width in self.segmentWidthsArray) {
                        if (idx == i)
                            break;
                        xOffset = xOffset + [width floatValue];
                        i++;
                    }
                    
                    CGFloat widthForIndex = [[self.segmentWidthsArray objectAtIndex:idx] floatValue];
                    rect = CGRectMake(xOffset, y, widthForIndex, stringHeight);
                    fullRect = CGRectMake(self.segmentWidth * idx, 0, widthForIndex, oldRect.size.height);
                    rectDiv = CGRectMake(xOffset - (self.verticalDividerWidth / 2), self.selectionIndicatorHeight * 2, self.verticalDividerWidth, self.frame.size.height - (self.selectionIndicatorHeight * 4));
                }
                
                // Fix rect position/size to avoid blurry labels
                rect = CGRectMake(ceilf(rect.origin.x), ceilf(rect.origin.y), ceilf(rect.size.width), ceilf(rect.size.height));
                
                //不用label 用 catextlayer
                CATextLayer *titleLayer = [CATextLayer layer];
                titleLayer.frame = rect;
                titleLayer.alignmentMode = kCAAlignmentCenter;
                if ([UIDevice currentDevice].systemVersion.floatValue < 10.0 ) {
                    titleLayer.truncationMode = kCATruncationEnd;
                }
                titleLayer.string = [self attributedTitleAtIndex:idx];
                titleLayer.contentsScale = [[UIScreen mainScreen] scale];
                
                [self.scrollView.layer addSublayer:titleLayer];
                
                // Vertical Divider
                // 判断是否显示分割线
                if (self.isVerticalDividerEnabled && idx > 0) {
                    CALayer *verticalDividerLayer = [CALayer layer];
                    verticalDividerLayer.frame = rectDiv;
                    verticalDividerLayer.backgroundColor = self.verticalDividerColor.CGColor;
                    
                    [self.scrollView.layer addSublayer:verticalDividerLayer];
                }
            
                [self addBackgroundAndBorderLayerWithRect:fullRect];
            }];
        } else if (self.type == HMSegmentedControlTypeImages) {
            //当segment的type 等于图片的时候
            [self.sectionImages enumerateObjectsUsingBlock:^(id iconImage, NSUInteger idx, BOOL *stop) {
                UIImage *icon = iconImage;
                CGFloat imageWidth = icon.size.width;
                CGFloat imageHeight = icon.size.height;
                CGFloat y = roundf(CGRectGetHeight(self.frame) - self.selectionIndicatorHeight) / 2 - imageHeight / 2 + ((self.selectionIndicatorLocation == HMSegmentedControlSelectionIndicatorLocationUp) ? self.selectionIndicatorHeight : 0);
                CGFloat x = self.segmentWidth * idx + (self.segmentWidth - imageWidth)/2.0f;
                CGRect rect = CGRectMake(x, y, imageWidth, imageHeight);
                
                CALayer *imageLayer = [CALayer layer];
                imageLayer.frame = rect;
                
                if (self.selectedSegmentIndex == idx) {
                    if (self.sectionSelectedImages) {
                        UIImage *highlightIcon = [self.sectionSelectedImages objectAtIndex:idx];
                        imageLayer.contents = (id)highlightIcon.CGImage;
                    } else {
                        imageLayer.contents = (id)icon.CGImage;
                    }
                } else {
                    imageLayer.contents = (id)icon.CGImage;
                }
                
                [self.scrollView.layer addSublayer:imageLayer];
                // Vertical Divider
                if (self.isVerticalDividerEnabled && idx>0) {
                    CALayer *verticalDividerLayer = [CALayer layer];
                    verticalDividerLayer.frame = CGRectMake((self.segmentWidth * idx) - (self.verticalDividerWidth / 2), self.selectionIndicatorHeight * 2, self.verticalDividerWidth, self.frame.size.height-(self.selectionIndicatorHeight * 4));
                    verticalDividerLayer.backgroundColor = self.verticalDividerColor.CGColor;
                    
                    [self.scrollView.layer addSublayer:verticalDividerLayer];
                }
                
                [self addBackgroundAndBorderLayerWithRect:rect];
            }];
        } else if (self.type == HMSegmentedControlTypeTextImages){
            //当segment的type 等于图文的时候
            [self.sectionImages enumerateObjectsUsingBlock:^(id iconImage, NSUInteger idx, BOOL *stop) {
                UIImage *icon = iconImage;
                CGFloat imageWidth = icon.size.width;
                CGFloat imageHeight = icon.size.height;
                
                CGFloat stringHeight = [self measureTitleAtIndex:idx].height;
                CGFloat yOffset = roundf(((CGRectGetHeight(self.frame) - self.selectionIndicatorHeight) / 2) - (stringHeight / 2));
                
                CGFloat imageXOffset = self.segmentEdgeInset.left; // Start with edge inset
                CGFloat textXOffset  = self.segmentEdgeInset.left;
                CGFloat textWidth = 0;
                
                if (self.segmentWidthStyle == HMSegmentedControlSegmentWidthStyleFixed) {
                    imageXOffset = (self.segmentWidth * idx) + (self.segmentWidth / 2.0f) - (imageWidth / 2.0f);
                    textXOffset = self.segmentWidth * idx;
                    textWidth = self.segmentWidth;
                } else if (self.segmentWidthStyle == HMSegmentedControlSegmentWidthStyleDynamic) {
                    // When we are drawing dynamic widths, we need to loop the widths array to calculate the xOffset
                    CGFloat xOffset = 0;
                    NSInteger i = 0;
                    
                    for (NSNumber *width in self.segmentWidthsArray) {
                        if (idx == i) {
                            break;
                        }
                        
                        xOffset = xOffset + [width floatValue];
                        i++;
                    }
                    
                    imageXOffset = xOffset + ([self.segmentWidthsArray[idx] floatValue] / 2.0f) - (imageWidth / 2.0f); //(self.segmentWidth / 2.0f) - (imageWidth / 2.0f)
                    textXOffset = xOffset;
                    textWidth = [self.segmentWidthsArray[idx] floatValue];
                }
                
                CGFloat imageYOffset = roundf((CGRectGetHeight(self.frame) - self.selectionIndicatorHeight) / 2.0f);
                CGRect imageRect = CGRectMake(imageXOffset, imageYOffset, imageWidth, imageHeight);
                CGRect textRect = CGRectMake(textXOffset, yOffset, textWidth, stringHeight);
                
                // Fix rect position/size to avoid blurry labels
                textRect = CGRectMake(ceilf(textRect.origin.x), ceilf(textRect.origin.y), ceilf(textRect.size.width), ceilf(textRect.size.height));
    
                CATextLayer *titleLayer = [CATextLayer layer];
                titleLayer.frame = textRect;
                titleLayer.alignmentMode = kCAAlignmentCenter;
                titleLayer.string = [self attributedTitleAtIndex:idx];
                if ([UIDevice currentDevice].systemVersion.floatValue < 10.0 ) {
                    titleLayer.truncationMode = kCATruncationEnd;
                }
                CALayer *imageLayer = [CALayer layer];
                imageLayer.frame = imageRect;
                
                if (self.selectedSegmentIndex == idx) {
                    if (self.sectionSelectedImages) {
                        UIImage *highlightIcon = [self.sectionSelectedImages objectAtIndex:idx];
                        imageLayer.contents = (id)highlightIcon.CGImage;
                    } else {
                        imageLayer.contents = (id)icon.CGImage;
                    }
                } else {
                    imageLayer.contents = (id)icon.CGImage;
                }
                
                [self.scrollView.layer addSublayer:imageLayer];
                titleLayer.contentsScale = [[UIScreen mainScreen] scale];
                [self.scrollView.layer addSublayer:titleLayer];
                
                [self addBackgroundAndBorderLayerWithRect:imageRect];
            }];
        }
        
        // Add the selection indicators
        // 添加选中的指示器
        if (self.selectedSegmentIndex != HMSegmentedControlNoSegment) {
            if (self.selectionStyle == HMSegmentedControlSelectionStyleArrow) {
                if (!self.selectionIndicatorArrowLayer.superlayer) {
                    [self setArrowFrame];
                    [self.scrollView.layer addSublayer:self.selectionIndicatorArrowLayer];
                }
            } else {
                if (!self.selectionIndicatorStripLayer.superlayer) {
                    self.selectionIndicatorStripLayer.frame = [self frameForSelectionIndicator];
                    [self.scrollView.layer addSublayer:self.selectionIndicatorStripLayer];
                    
                    if (self.selectionStyle == HMSegmentedControlSelectionStyleBox && !self.selectionIndicatorBoxLayer.superlayer) {
                        self.selectionIndicatorBoxLayer.frame = [self frameForFillerSelectionIndicator];
                        [self.scrollView.layer insertSublayer:self.selectionIndicatorBoxLayer atIndex:0];
                    }
                }
            }
        }
    }
    
    
    /**
     计算title的cgsize
    
     @param index 位置
     @return 宽高
     */
    - (CGSize)measureTitleAtIndex:(NSUInteger)index {
        if (index >= self.sectionTitles.count) {
            return CGSizeZero;
        }
        
        id title = self.sectionTitles[index];
        CGSize size = CGSizeZero;
        
        // 判断index是否和当前选中的index相等
        BOOL selected = (index == self.selectedSegmentIndex) ? YES : NO;
        
        if ([title isKindOfClass:[NSString class]] && !self.titleFormatter) {
            // 当title是string类型 而且formatter不存在的时候
            // 选中与非选中的时候返回一个富文本
            NSDictionary *titleAttrs = selected ? [self resultingSelectedTitleTextAttributes] : [self resultingTitleTextAttributes];
            size = [(NSString *)title sizeWithAttributes:titleAttrs];//计算文字经过富文本处理后的size
        } else if ([title isKindOfClass:[NSString class]] && self.titleFormatter) {
            // 当title是string类型 而且富文本存在的时候
            size = [self.titleFormatter(self, title, index, selected) size];
        } else if ([title isKindOfClass:[NSAttributedString class]]) {
            size = [(NSAttributedString *)title size];
        } else {
            NSAssert(title == nil, @"Unexpected type of segment title: %@", [title class]);
            size = CGSizeZero;
        }
        
        // 返回结果的最小矩形将源矩形值转换为整数。
        return CGRectIntegral((CGRect){CGPointZero, size}).size;
    }
    
    
    /**
     返回一个富文本string
    
     @param index index
     @return 富文本string
     */
    - (NSAttributedString *)attributedTitleAtIndex:(NSUInteger)index {
        id title = self.sectionTitles[index];
        BOOL selected = (index == self.selectedSegmentIndex) ? YES : NO;
        
        if ([title isKindOfClass:[NSAttributedString class]]) {
            return (NSAttributedString *)title;
        } else if (!self.titleFormatter) {
            NSDictionary *titleAttrs = selected ? [self resultingSelectedTitleTextAttributes] : [self resultingTitleTextAttributes];
            
            // the color should be cast to CGColor in order to avoid invalid context on iOS7
            UIColor *titleColor = titleAttrs[NSForegroundColorAttributeName];
            
            if (titleColor) {
                NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithDictionary:titleAttrs];
                
                dict[NSForegroundColorAttributeName] = (id)titleColor.CGColor;
                
                titleAttrs = [NSDictionary dictionaryWithDictionary:dict];
            }
            
            return [[NSAttributedString alloc] initWithString:(NSString *)title attributes:titleAttrs];
        } else {
            return self.titleFormatter(self, title, index, selected);
        }
    }
    
    
    /**
     添加背景和border 的layer
    
     @param fullRect cgrect
     */
    - (void)addBackgroundAndBorderLayerWithRect:(CGRect)fullRect {
        // Background layer
        CALayer *backgroundLayer = [CALayer layer];
        backgroundLayer.frame = fullRect;
        [self.layer insertSublayer:backgroundLayer atIndex:0];
        
        // Border layer
        if (self.borderType & HMSegmentedControlBorderTypeTop) {
            CALayer *borderLayer = [CALayer layer];
            borderLayer.frame = CGRectMake(0, 0, fullRect.size.width, self.borderWidth);
            borderLayer.backgroundColor = self.borderColor.CGColor;
            [backgroundLayer addSublayer: borderLayer];
        }
        if (self.borderType & HMSegmentedControlBorderTypeLeft) {
            CALayer *borderLayer = [CALayer layer];
            borderLayer.frame = CGRectMake(0, 0, self.borderWidth, fullRect.size.height);
            borderLayer.backgroundColor = self.borderColor.CGColor;
            [backgroundLayer addSublayer: borderLayer];
        }
        if (self.borderType & HMSegmentedControlBorderTypeBottom) {
            CALayer *borderLayer = [CALayer layer];
            borderLayer.frame = CGRectMake(0, fullRect.size.height - self.borderWidth, fullRect.size.width, self.borderWidth);
            borderLayer.backgroundColor = self.borderColor.CGColor;
            [backgroundLayer addSublayer: borderLayer];
        }
        if (self.borderType & HMSegmentedControlBorderTypeRight) {
            CALayer *borderLayer = [CALayer layer];
            borderLayer.frame = CGRectMake(fullRect.size.width - self.borderWidth, 0, self.borderWidth, fullRect.size.height);
            borderLayer.backgroundColor = self.borderColor.CGColor;
            [backgroundLayer addSublayer: borderLayer];
        }
    }
    
    
    /**
     设置箭头指示器
     */
    - (void)setArrowFrame {
        self.selectionIndicatorArrowLayer.frame = [self frameForSelectionIndicator];
        
        self.selectionIndicatorArrowLayer.mask = nil;
        
        UIBezierPath *arrowPath = [UIBezierPath bezierPath];
        
        CGPoint p1 = CGPointZero;
        CGPoint p2 = CGPointZero;
        CGPoint p3 = CGPointZero;
        
        if (self.selectionIndicatorLocation == HMSegmentedControlSelectionIndicatorLocationDown) {
            p1 = CGPointMake(self.selectionIndicatorArrowLayer.bounds.size.width / 2, 0);
            p2 = CGPointMake(0, self.selectionIndicatorArrowLayer.bounds.size.height);
            p3 = CGPointMake(self.selectionIndicatorArrowLayer.bounds.size.width, self.selectionIndicatorArrowLayer.bounds.size.height);
        }
        
        if (self.selectionIndicatorLocation == HMSegmentedControlSelectionIndicatorLocationUp) {
            p1 = CGPointMake(self.selectionIndicatorArrowLayer.bounds.size.width / 2, self.selectionIndicatorArrowLayer.bounds.size.height);
            p2 = CGPointMake(self.selectionIndicatorArrowLayer.bounds.size.width, 0);
            p3 = CGPointMake(0, 0);
        }
        
        [arrowPath moveToPoint:p1];
        [arrowPath addLineToPoint:p2];
        [arrowPath addLineToPoint:p3];
        [arrowPath closePath];
        
        CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
        maskLayer.frame = self.selectionIndicatorArrowLayer.bounds;
        maskLayer.path = arrowPath.CGPath;
        self.selectionIndicatorArrowLayer.mask = maskLayer;
    }
    
    
    /**
     计算选中指示器的rect
    
     @return rect
     */
    - (CGRect)frameForSelectionIndicator {
        CGFloat indicatorYOffset = 0.0f;
        
        // 类型是向下
        if (self.selectionIndicatorLocation == HMSegmentedControlSelectionIndicatorLocationDown) {
            indicatorYOffset = self.bounds.size.height - self.selectionIndicatorHeight + self.selectionIndicatorEdgeInsets.bottom;
        }
        
        // 类型是向上
        if (self.selectionIndicatorLocation == HMSegmentedControlSelectionIndicatorLocationUp) {
            indicatorYOffset = self.selectionIndicatorEdgeInsets.top;
        }
        
        CGFloat sectionWidth = 0.0f;
        
        // 文字类型
        if (self.type == HMSegmentedControlTypeText) {
            CGFloat stringWidth = [self measureTitleAtIndex:self.selectedSegmentIndex].width;
            sectionWidth = stringWidth;
        } else if (self.type == HMSegmentedControlTypeImages) {
            // 图片类型
            UIImage *sectionImage = [self.sectionImages objectAtIndex:self.selectedSegmentIndex];
            CGFloat imageWidth = sectionImage.size.width;
            sectionWidth = imageWidth;
        } else if (self.type == HMSegmentedControlTypeTextImages) {
            // 图文类型
            CGFloat stringWidth = [self measureTitleAtIndex:self.selectedSegmentIndex].width;
            UIImage *sectionImage = [self.sectionImages objectAtIndex:self.selectedSegmentIndex];
            CGFloat imageWidth = sectionImage.size.width;
            sectionWidth = MAX(stringWidth, imageWidth);
        }
        
        if (self.selectionStyle == HMSegmentedControlSelectionStyleArrow) {
            // 箭头
            CGFloat widthToEndOfSelectedSegment = (self.segmentWidth * self.selectedSegmentIndex) + self.segmentWidth;
            CGFloat widthToStartOfSelectedIndex = (self.segmentWidth * self.selectedSegmentIndex);
            
            CGFloat x = widthToStartOfSelectedIndex + ((widthToEndOfSelectedSegment - widthToStartOfSelectedIndex) / 2) - (self.selectionIndicatorHeight/2);
            return CGRectMake(x - (self.selectionIndicatorHeight / 2), indicatorYOffset, self.selectionIndicatorHeight * 2, self.selectionIndicatorHeight);
        } else {
            if (self.selectionStyle == HMSegmentedControlSelectionStyleTextWidthStripe &&
                sectionWidth <= self.segmentWidth &&
                self.segmentWidthStyle != HMSegmentedControlSegmentWidthStyleDynamic) {
                CGFloat widthToEndOfSelectedSegment = (self.segmentWidth * self.selectedSegmentIndex) + self.segmentWidth;
                CGFloat widthToStartOfSelectedIndex = (self.segmentWidth * self.selectedSegmentIndex);
                
                CGFloat x = ((widthToEndOfSelectedSegment - widthToStartOfSelectedIndex) / 2) + (widthToStartOfSelectedIndex - sectionWidth / 2);
                return CGRectMake(x + self.selectionIndicatorEdgeInsets.left, indicatorYOffset, sectionWidth - self.selectionIndicatorEdgeInsets.right, self.selectionIndicatorHeight);
            } else {
                if (self.segmentWidthStyle == HMSegmentedControlSegmentWidthStyleDynamic) {
                    CGFloat selectedSegmentOffset = 0.0f;
                    
                    NSInteger i = 0;
                    for (NSNumber *width in self.segmentWidthsArray) {
                        if (self.selectedSegmentIndex == i)
                            break;
                        selectedSegmentOffset = selectedSegmentOffset + [width floatValue];
                        i++;
                    }
                    return CGRectMake(selectedSegmentOffset + self.selectionIndicatorEdgeInsets.left, indicatorYOffset, [[self.segmentWidthsArray objectAtIndex:self.selectedSegmentIndex] floatValue] - self.selectionIndicatorEdgeInsets.right, self.selectionIndicatorHeight + self.selectionIndicatorEdgeInsets.bottom);
                }
                
                return CGRectMake((self.segmentWidth + self.selectionIndicatorEdgeInsets.left) * self.selectedSegmentIndex, indicatorYOffset, self.segmentWidth - self.selectionIndicatorEdgeInsets.right, self.selectionIndicatorHeight);
            }
        }
    }
    
    
    /**
     根据宽度类型
    
     @return rect
     */
    - (CGRect)frameForFillerSelectionIndicator {
        if (self.segmentWidthStyle == HMSegmentedControlSegmentWidthStyleDynamic) {
            CGFloat selectedSegmentOffset = 0.0f;
            
            NSInteger i = 0;
            for (NSNumber *width in self.segmentWidthsArray) {
                if (self.selectedSegmentIndex == i) {
                    break;
                }
                selectedSegmentOffset = selectedSegmentOffset + [width floatValue];
                
                i++;
            }
            
            return CGRectMake(selectedSegmentOffset, 0, [[self.segmentWidthsArray objectAtIndex:self.selectedSegmentIndex] floatValue], CGRectGetHeight(self.frame));
        }
        return CGRectMake(self.segmentWidth * self.selectedSegmentIndex, 0, self.segmentWidth, CGRectGetHeight(self.frame));
    }
    
    
    /**
     更新rect
     */
    - (void)updateSegmentsRects {
        self.scrollView.contentInset = UIEdgeInsetsZero;
        self.scrollView.frame = CGRectMake(0, 0, CGRectGetWidth(self.frame), CGRectGetHeight(self.frame));
        
        if ([self sectionCount] > 0) {
            self.segmentWidth = self.frame.size.width / [self sectionCount];
        }
        
        if (self.type == HMSegmentedControlTypeText && self.segmentWidthStyle == HMSegmentedControlSegmentWidthStyleFixed) {
            [self.sectionTitles enumerateObjectsUsingBlock:^(id titleString, NSUInteger idx, BOOL *stop) {
                CGFloat stringWidth = [self measureTitleAtIndex:idx].width + self.segmentEdgeInset.left + self.segmentEdgeInset.right;
                self.segmentWidth = MAX(stringWidth, self.segmentWidth);
            }];
        } else if (self.type == HMSegmentedControlTypeText && self.segmentWidthStyle == HMSegmentedControlSegmentWidthStyleDynamic) {
            NSMutableArray *mutableSegmentWidths = [NSMutableArray array];
            
            [self.sectionTitles enumerateObjectsUsingBlock:^(id titleString, NSUInteger idx, BOOL *stop) {
                CGFloat stringWidth = [self measureTitleAtIndex:idx].width + self.segmentEdgeInset.left + self.segmentEdgeInset.right;
                [mutableSegmentWidths addObject:[NSNumber numberWithFloat:stringWidth]];
            }];
            self.segmentWidthsArray = [mutableSegmentWidths copy];
        } else if (self.type == HMSegmentedControlTypeImages) {
            for (UIImage *sectionImage in self.sectionImages) {
                CGFloat imageWidth = sectionImage.size.width + self.segmentEdgeInset.left + self.segmentEdgeInset.right;
                self.segmentWidth = MAX(imageWidth, self.segmentWidth);
            }
        } else if (self.type == HMSegmentedControlTypeTextImages && self.segmentWidthStyle == HMSegmentedControlSegmentWidthStyleFixed){
            //lets just use the title.. we will assume it is wider then images...
            [self.sectionTitles enumerateObjectsUsingBlock:^(id titleString, NSUInteger idx, BOOL *stop) {
                CGFloat stringWidth = [self measureTitleAtIndex:idx].width + self.segmentEdgeInset.left + self.segmentEdgeInset.right;
                self.segmentWidth = MAX(stringWidth, self.segmentWidth);
            }];
        } else if (self.type == HMSegmentedControlTypeTextImages && self.segmentWidthStyle == HMSegmentedControlSegmentWidthStyleDynamic) {
            NSMutableArray *mutableSegmentWidths = [NSMutableArray array];
            
            int i = 0;
            [self.sectionTitles enumerateObjectsUsingBlock:^(id titleString, NSUInteger idx, BOOL *stop) {
                CGFloat stringWidth = [self measureTitleAtIndex:idx].width + self.segmentEdgeInset.right;
                UIImage *sectionImage = [self.sectionImages objectAtIndex:i];
                CGFloat imageWidth = sectionImage.size.width + self.segmentEdgeInset.left;
                
                CGFloat combinedWidth = MAX(imageWidth, stringWidth);
                
                [mutableSegmentWidths addObject:[NSNumber numberWithFloat:combinedWidth]];
            }];
            self.segmentWidthsArray = [mutableSegmentWidths copy];
        }
    
        self.scrollView.scrollEnabled = self.isUserDraggable;
        self.scrollView.contentSize = CGSizeMake([self totalSegmentedControlWidth], self.frame.size.height);
    }
    
    
    /**
     section的个数
    
     @return 个数
     */
    - (NSUInteger)sectionCount {
        if (self.type == HMSegmentedControlTypeText) {
            return self.sectionTitles.count;
        } else if (self.type == HMSegmentedControlTypeImages ||
                   self.type == HMSegmentedControlTypeTextImages) {
            return self.sectionImages.count;
        }
        
        return 0;
    }
    
    
    /**
     重写view的时候可能用到方法
    
     @param newSuperview 新view
     */
    - (void)willMoveToSuperview:(UIView *)newSuperview {
        // Control is being removed
        if (newSuperview == nil)
            return;
        
        if (self.sectionTitles || self.sectionImages) {
            [self updateSegmentsRects];
        }
    }
    
    #pragma mark - Touch
    
    - (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
        UITouch *touch = [touches anyObject];
        CGPoint touchLocation = [touch locationInView:self];
        
        CGRect enlargeRect =   CGRectMake(self.bounds.origin.x - self.enlargeEdgeInset.left,
                          self.bounds.origin.y - self.enlargeEdgeInset.top,
                          self.bounds.size.width + self.enlargeEdgeInset.left + self.enlargeEdgeInset.right,
                          self.bounds.size.height + self.enlargeEdgeInset.top + self.enlargeEdgeInset.bottom);
        
        if (CGRectContainsPoint(enlargeRect, touchLocation)) {
            NSInteger segment = 0;
            if (self.segmentWidthStyle == HMSegmentedControlSegmentWidthStyleFixed) {
                segment = (touchLocation.x + self.scrollView.contentOffset.x) / self.segmentWidth;
            } else if (self.segmentWidthStyle == HMSegmentedControlSegmentWidthStyleDynamic) {
                // To know which segment the user touched, we need to loop over the widths and substract it from the x position.
                CGFloat widthLeft = (touchLocation.x + self.scrollView.contentOffset.x);
                for (NSNumber *width in self.segmentWidthsArray) {
                    widthLeft = widthLeft - [width floatValue];
                    
                    // When we don't have any width left to substract, we have the segment index.
                    if (widthLeft <= 0)
                        break;
                    
                    segment++;
                }
            }
            
            NSUInteger sectionsCount = 0;
            
            if (self.type == HMSegmentedControlTypeImages) {
                sectionsCount = [self.sectionImages count];
            } else if (self.type == HMSegmentedControlTypeTextImages || self.type == HMSegmentedControlTypeText) {
                sectionsCount = [self.sectionTitles count];
            }
            
            if (segment != self.selectedSegmentIndex && segment < sectionsCount) {
                // Check if we have to do anything with the touch event
                if (self.isTouchEnabled)
                    [self setSelectedSegmentIndex:segment animated:self.shouldAnimateUserSelection notify:YES];
            }
        }
    }
    
    #pragma mark - Scrolling
    /**
     可以滚动的范围width
    
     @return float
     */
    - (CGFloat)totalSegmentedControlWidth {
        if (self.type == HMSegmentedControlTypeText && self.segmentWidthStyle == HMSegmentedControlSegmentWidthStyleFixed) {
            // 因为每个宽度一样所以返回文字个数*segment宽度
            return self.sectionTitles.count * self.segmentWidth;
        } else if (self.segmentWidthStyle == HMSegmentedControlSegmentWidthStyleDynamic) {
            // 返回所有文字宽度的和
            return [[self.segmentWidthsArray valueForKeyPath:@"@sum.self"] floatValue];
        } else {
            return self.sectionImages.count * self.segmentWidth;
        }
    }
    
    
    /**
     滚动到选中的segment
    
     @param animated 动画
     */
    - (void)scrollToSelectedSegmentIndex:(BOOL)animated {
        CGRect rectForSelectedIndex = CGRectZero;
        CGFloat selectedSegmentOffset = 0;
        if (self.segmentWidthStyle == HMSegmentedControlSegmentWidthStyleFixed) {
            rectForSelectedIndex = CGRectMake(self.segmentWidth * self.selectedSegmentIndex,
                                              0,
                                              self.segmentWidth,
                                              self.frame.size.height);
            
            selectedSegmentOffset = (CGRectGetWidth(self.frame) / 2) - (self.segmentWidth / 2);
        } else {
            NSInteger i = 0;
            CGFloat offsetter = 0;
            for (NSNumber *width in self.segmentWidthsArray) {
                if (self.selectedSegmentIndex == i)
                    break;
                offsetter = offsetter + [width floatValue];
                i++;
            }
            
            rectForSelectedIndex = CGRectMake(offsetter,
                                              0,
                                              [[self.segmentWidthsArray objectAtIndex:self.selectedSegmentIndex] floatValue],
                                              self.frame.size.height);
            
            selectedSegmentOffset = (CGRectGetWidth(self.frame) / 2) - ([[self.segmentWidthsArray objectAtIndex:self.selectedSegmentIndex] floatValue] / 2);
        }
        
        
        CGRect rectToScrollTo = rectForSelectedIndex;
        rectToScrollTo.origin.x -= selectedSegmentOffset;
        rectToScrollTo.size.width += selectedSegmentOffset * 2;
        [self.scrollView scrollRectToVisible:rectToScrollTo animated:animated];
    }
    
    #pragma mark - Index Change
    
    - (void)setSelectedSegmentIndex:(NSInteger)index {
        [self setSelectedSegmentIndex:index animated:NO notify:NO];
    }
    
    - (void)setSelectedSegmentIndex:(NSUInteger)index animated:(BOOL)animated {
        [self setSelectedSegmentIndex:index animated:animated notify:NO];
    }
    
    /**
     设置选中的segment的位置
    
     @param index 位置
     @param animated 动画
     @param notify 是否回调
     */
    - (void)setSelectedSegmentIndex:(NSUInteger)index animated:(BOOL)animated notify:(BOOL)notify {
        _selectedSegmentIndex = index;
        [self setNeedsDisplay];
        
        if (index == HMSegmentedControlNoSegment) {
            // 当类型为无的时候移除 指示器的layer
            [self.selectionIndicatorArrowLayer removeFromSuperlayer];
            [self.selectionIndicatorStripLayer removeFromSuperlayer];
            [self.selectionIndicatorBoxLayer removeFromSuperlayer];
        } else {
            [self scrollToSelectedSegmentIndex:animated];
            
            if (animated) {
                // If the selected segment layer is not added to the super layer, that means no
                // index is currently selected, so add the layer then move it to the new
                // segment index without animating.
                if(self.selectionStyle == HMSegmentedControlSelectionStyleArrow) {
                    if ([self.selectionIndicatorArrowLayer superlayer] == nil) {
                        [self.scrollView.layer addSublayer:self.selectionIndicatorArrowLayer];
                        
                        [self setSelectedSegmentIndex:index animated:NO notify:YES];
                        return;
                    }
                }
                else {
                    if ([self.selectionIndicatorStripLayer superlayer] == nil) {
                        [self.scrollView.layer addSublayer:self.selectionIndicatorStripLayer];
                        
                        if (self.selectionStyle == HMSegmentedControlSelectionStyleBox && [self.selectionIndicatorBoxLayer superlayer] == nil)
                            [self.scrollView.layer insertSublayer:self.selectionIndicatorBoxLayer atIndex:0];
                        
                        [self setSelectedSegmentIndex:index animated:NO notify:YES];
                        return;
                    }
                }
                
                if (notify)
                    [self notifyForSegmentChangeToIndex:index];
                
                // Restore CALayer animations
                // 恢复layer的动画
                self.selectionIndicatorArrowLayer.actions = nil;
                self.selectionIndicatorStripLayer.actions = nil;
                self.selectionIndicatorBoxLayer.actions = nil;
                
                // Animate to new position
                //移动到新的位置
                [CATransaction begin];
                [CATransaction setAnimationDuration:0.15f];
                [CATransaction setAnimationTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]];
                [self setArrowFrame];
                self.selectionIndicatorBoxLayer.frame = [self frameForSelectionIndicator];
                self.selectionIndicatorStripLayer.frame = [self frameForSelectionIndicator];
                self.selectionIndicatorBoxLayer.frame = [self frameForFillerSelectionIndicator];
                [CATransaction commit];
            } else {
                // Disable CALayer animations
                NSMutableDictionary *newActions = [[NSMutableDictionary alloc] initWithObjectsAndKeys:[NSNull null], @"position", [NSNull null], @"bounds", nil];
                self.selectionIndicatorArrowLayer.actions = newActions;
                [self setArrowFrame];
                
                self.selectionIndicatorStripLayer.actions = newActions;
                self.selectionIndicatorStripLayer.frame = [self frameForSelectionIndicator];
                
                self.selectionIndicatorBoxLayer.actions = newActions;
                self.selectionIndicatorBoxLayer.frame = [self frameForFillerSelectionIndicator];
                
                if (notify)
                    [self notifyForSegmentChangeToIndex:index];
            }
        }
    }
    
    /**
     回调与触发UIControlEventValueChanged的通知
    
     @param index index
     */
    - (void)notifyForSegmentChangeToIndex:(NSInteger)index {
        if (self.superview)
            [self sendActionsForControlEvents:UIControlEventValueChanged];
        
        if (self.indexChangeBlock)
            self.indexChangeBlock(index);
    }
    
    #pragma mark - Styling Support
    /**
     title富文本
    
     @return 字典
     */
    - (NSDictionary *)resultingTitleTextAttributes {
        //默认的富文本
        NSDictionary *defaults = @{
            NSFontAttributeName : [UIFont systemFontOfSize:19.0f],
            NSForegroundColorAttributeName : [UIColor blackColor],
        };
        
        NSMutableDictionary *resultingAttrs = [NSMutableDictionary dictionaryWithDictionary:defaults];
        
        if (self.titleTextAttributes) {
            //拼接两个字典
            [resultingAttrs addEntriesFromDictionary:self.titleTextAttributes];
        }
    
        return [resultingAttrs copy];
    }
    
    
    /**
     返回选中文字的富文本
    
     @return 字典
     */
    - (NSDictionary *)resultingSelectedTitleTextAttributes {
        NSMutableDictionary *resultingAttrs = [NSMutableDictionary dictionaryWithDictionary:[self resultingTitleTextAttributes]];
        
        if (self.selectedTitleTextAttributes) {
            [resultingAttrs addEntriesFromDictionary:self.selectedTitleTextAttributes];
        }
        
        return [resultingAttrs copy];
    }
    
    @end
    
    

    相关文章

      网友评论

      本文标题:HMSegmentedControl源代码解析

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