HMSegmentedControl源码阅读
0.一些准备
typedef void (^IndexChangeBlock)(NSInteger index);//block的typedef 无返回值,参数nsinterger ,新别名IndexChangeBlock
typedef NSAttributedString *(^HMTitleFormatterBlock)(HMSegmentedControl *segmentedControl, NSString *title, NSUInteger index, BOOL selected);//返回值NSAttributedString类型,是一种带有属性的字符串
当改变selected index时执行的block,也可以使用addTarget:action:forControlEvents:方式来替代block的执行:
@property (nonatomic, copy) IndexChangeBlock indexChangeBlock;
用来设置segmentControl上文本的样式的block:
@property (nonatomic, copy) HMTitleFormatterBlock titleFormatter;
一些枚举:
HMSegmentedControlSelectionStyle:indicator类型。indicator和文本等宽(含inset)、和segment一样宽,背景大方块,箭头
HMSegmentedControlSelectionIndicatorLocation:indicator位置
HMSegmentedControlSegmentWidthStyle:segment宽度类型
HMSegmentedControlType:segmentControl类型
//indicator条纹样式图层
@property (nonatomic, strong) CALayer *selectionIndicatorStripLayer;
//indicator方块样式图层
@property (nonatomic, strong) CALayer *selectionIndicatorBoxLayer;
//indicator箭头样式图层
@property (nonatomic, strong) CALayer *selectionIndicatorArrowLayer;
//HMSegmentedControlSegmentWidthStyleFixed类型时的segment宽度,每个都等宽
@property (nonatomic, readwrite) CGFloat segmentWidth;
//HMSegmentedControlSegmentWidthStyleDynamic类型时的宽度数组
@property (nonatomic, readwrite) NSArray *segmentWidthsArray;
//HMScrollView继承自UIscrollview,是实现HMSegmentedControl的主体控件
@property (nonatomic, strong) HMScrollView *scrollView;
另外,
HMSegmentedControl继承自UIControl类
@interface HMSegmentedControl : UIControl
1.HMScrollView
HMScrollView是实现HMSegmentedControl的主体控件
@interface HMScrollView : UIScrollView
@end
@implementation HMScrollView
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
// If not dragging, send event to next responder
if (!self.dragging) {
[self.nextResponder touchesBegan:touches withEvent:event];
} else {
[super touchesBegan:touches withEvent:event];
}
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
if (!self.dragging) {
[self.nextResponder touchesMoved:touches withEvent:event];
} else{
[super touchesMoved:touches withEvent:event];
}
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
if (!self.dragging) {
[self.nextResponder touchesEnded:touches withEvent:event];
} else {
[super touchesEnded:touches withEvent:event];
}
}
@end
这里scrollview的设计:自己不处理touches事件,是把touches事件转发给下一个响应者(父视图 即self)来处理。因为scrollview会“屏蔽”掉下个响应者的touches事件,即单纯的[super touches...]不能把事件转发给下个响应者https://stackoverflow.com/questions/7439273/uiscrollview-prevents-touchesbegan-touchesmoved-touchesended-on-view-controlle
2.初始化方法
HMSegmentedControl的初始化方法
- (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;
}
要同时支持 initWithFrame 和 initWithCoder ,那么可以提供一个 commonInit 方法来做统一的初始化.
//segmentControl内容是文字类型
- (id)initWithSectionTitles:(NSArray *)sectiontitles {
self = [self initWithFrame:CGRectZero];//之后再通过实例对象 另外设置frame
if (self) {
[self commonInit];
self.sectionTitles = sectiontitles;
self.type = HMSegmentedControlTypeText;
}
return self;
}
//图片类型
- (id)initWithSectionImages:(NSArray*)sectionImages sectionSelectedImages:(NSArray*)sectionSelectedImages {
......
}
//图文类型
- (instancetype)initWithSectionImages:(NSArray *)sectionImages sectionSelectedImages:(NSArray *)sectionSelectedImages titlesForSections:(NSArray *)section titles {
......
}
//给属性初始化一些默认的值
- (void)commonInit {
self.scrollView = [[HMScrollView alloc] init];
self.scrollView.scrollsToTop = NO;
self.scrollView.showsVerticalScrollIndicator = NO;
self.scrollView.showsHorizontalScrollIndicator = NO;
[self addSubview:self.scrollView];//唯一一个addsubview
......
//indicator的边沿inset
self.selectionIndicatorEdgeInsets = UIEdgeInsetsMake(0.0f, 0.0f, 0.0f, 0.0f);
self.userDraggable = YES;//segmentControl是否可以滑动(当选项过多时会有左右滑动的需要)
self.touchEnabled = YES;//segment是否可以点击
self.verticalDividerEnabled = NO;//segment之间是否有竖分割线
self.shouldAnimateUserSelection = YES;//切换segment时indicator变化是否有动画
self.contentMode = UIViewContentModeRedraw;//将在每次设置或更改frame的时候自动调用drawRect:
}
-
如果你是直接基于 frame 来布局的,你应该确保在初始化的时候只添加视图,而不去设置它们的frame,把设置子视图 frame 的过程全部放到 layoutSubviews 方法里.如果你是基于 Auto Layout 约束来进行布局,那么可以在 commonInit 调用的时候就把约束添加上去,不要重写 layoutSubviews 方法,因为这种情况下它的默认实现就是根据约束来计算 frame。
-
通过设置contentMode属性值为UIViewContentModeRedraw。那么将在每次设置或更改frame的时候自动调用drawRect:。
其余UIViewContentMode(部分举例):
UIViewContentMode
3.segment上的文本字符串相关
1.计算文本字符串的size
- (CGSize)measureTitleAtIndex:(NSUInteger)index {
id title = self.sectionTitles[index];
CGSize size = CGSizeZero;
//该segment是否被选中?
BOOL selected = (index == self.selectedSegmentIndex) ? YES : NO;//selectedSegmentIndex 初始化的时候设为0
if ([title isKindOfClass:[NSString class]] && !self.titleFormatter) {//titleFormatter文本外观样式。为空 执行
//是否已经选中?分别对应不同的默认文本外观
NSDictionary *titleAttrs = selected ? [self resultingSelectedTitleTextAttributes] : [self resultingTitleTextAttributes];
size = [(NSString *)title sizeWithAttributes:titleAttrs];
} else if ([title isKindOfClass:[NSString class]] && self.titleFormatter) {//titleFormatter非空(已设置了样式)
size = [self.titleFormatter(self, title, index, selected) size];//使用设置的样式后获取size
} else if ([title isKindOfClass:[NSAttributedString class]]) {//如果title是NSAttributedString(带属性的字符串)
size = [(NSAttributedString *)title size];//直接获取size
} else {
NSAssert(title == nil, @"Unexpected type of segment title: %@", [title class]);
size = CGSizeZero;
}
return CGRectIntegral((CGRect){CGPointZero, size}).size;// 将矩形值转变成整数,得到一个最小的矩形
}
//非选中状态下的文本样式
- (NSDictionary *)resultingTitleTextAttributes {
NSDictionary *defaults = @{
NSFontAttributeName : [UIFont systemFontOfSize:19.0f],
NSForegroundColorAttributeName : [UIColor blackColor],
};
NSMutableDictionary *resultingAttrs = [NSMutableDictionary dictionaryWithDictionary:defaults];
if (self.titleTextAttributes) {//由外部设置titleTextAttributes
//addEntriesFromDictionary拼接字典,原字典已有的相同“键”,覆盖对应的键值对
[resultingAttrs addEntriesFromDictionary:self.titleTextAttributes];
}
return [resultingAttrs copy];
}
//选中状态下的文本样式
- (NSDictionary *)resultingSelectedTitleTextAttributes {
NSMutableDictionary *resultingAttrs = [NSMutableDictionary dictionaryWithDictionary:[self resultingTitleTextAttributes]];
if (self.selectedTitleTextAttributes) {
[resultingAttrs addEntriesFromDictionary:self.selectedTitleTextAttributes];
}
return [resultingAttrs copy];
}
- addEntriesFromDictionary:方法,拼接字典,如果原字典和新字典有相同“键”,用新字典覆盖对应的键值对。
- NSAttributedString叫做富文本,是一种带有属性的字符串,通过它可以轻松的在一个字符串中表现出多种字体、字号、字体大小等各不相同的风格。
简单使用,举例:
NSAttributedString *attString = [[NSAttributedString alloc] initWithString:@"title" attributes:@{NSForegroundColorAttributeName : [UIColor blueColor]}];
可变形式NSMutableAttributedString:
NSString * aString = @"this is a string";
NSMutableAttributedString * aAttributedString = [[NSMutableAttributedString alloc] initWithString:aString];
//文字颜色,range:作用范围前4个字符
[aAttributedString addAttribute:NSForegroundColorAttributeName value:[UIColor redColor] range:NSMakeRange(0, 4)];
//文字字体
[aAttributedString addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:25] range:NSMakeRange(0, 4)];
- 单行文本size的获取:
sizeWithAttributes:
多行文本size的获取:boundingRectWithSize: options: attributes: context:
2.返回某个index对应的文本它的带属性的字符串
- (NSAttributedString *)attributedTitleAtIndex:(NSUInteger)index {
......略
}
4.update layout
- (void)layoutSubviews {
[super layoutSubviews];
[self updateSegmentsRects];
}
- (void)setFrame:(CGRect)frame {
[super setFrame:frame];
[self updateSegmentsRects];
}
- (void)setSectionTitles:(NSArray *)sectionTitles {
_sectionTitles = sectionTitles;
[self setNeedsLayout];//不会马上刷新layout
}
- (void)setSectionImages:(NSArray *)sectionImages {
_sectionImages = sectionImages;
[self setNeedsLayout];
}
-
-setNeedsLayout:
在receiver标上一个需要被重新布局的标记,在系统runloop的下一个周期自动调用layoutSubviews。 -
layoutSubviews的调用时机:
-
其他相关方法:
1.layoutSubviews唯一调用到的方法:
该方法用于更新scrollview(HMScrollView)控件的一些属性contentInset、frame、scrollEnabled、content size,主要是frame和content size。以及计算每个segment的宽度。
//更新scrollview的一些属性contentInset、frame、scrollEnabled、contentsize
- (void)updateSegmentsRects {
self.scrollView.contentInset = UIEdgeInsetsZero;//scrollview的属性设置有部分放到了commoninit
self.scrollView.frame = CGRectMake(0, 0, CGRectGetWidth(self.frame), CGRectGetHeight(self.frame));
if ([self sectionCount] > 0) {//sectionCount:方法,计算segment个数
//计算segment宽度,均分
self.segmentWidth = self.frame.size.width / [self sectionCount];
}
//segmentCtrl是纯文本类型 segment宽是fix类型
if (self.type == HMSegmentedControlTypeText && self.segmentWidthStyle == HMSegmentedControlSegmentWidthStyleFixed) {
[self.sectionTitles enumerateObjectsUsingBlock:^(id titleString, NSUInteger idx, BOOL *stop) {
//计算字符串的宽度(含segment边沿左右inset)
CGFloat stringWidth = [self measureTitleAtIndex:idx].width + self.segmentEdgeInset.left + self.segmentEdgeInset.right;//Default is segmentEdgeInset:UIEdgeInsetsMake(0, 5, 0, 5)。
//均分宽度 和文本宽度 的最大值作为 segment宽度
self.segmentWidth = MAX(stringWidth, self.segmentWidth);
}];
}
//segmentCtrl是纯文本类型 segment宽是dynamic类型(和文本等宽,含inset)
else if (self.type == HMSegmentedControlTypeText && self.segmentWidthStyle == HMSegmentedControlSegmentWidthStyleDynamic) {
//数组存放每个segment的宽
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;//HMSegmentedControlSegmentWidthStyleDynamic类型下segment宽度的计算方法
[mutableSegmentWidths addObject:[NSNumber numberWithFloat:stringWidth]];
}];
self.segmentWidthsArray = [mutableSegmentWidths copy];
}
//segmentCtrl是图片类型
else if (self.type == HMSegmentedControlTypeImages) {
for (UIImage *sectionImage in self.sectionImages) {
//计算图片宽度,含inset
CGFloat imageWidth = sectionImage.size.width + self.segmentEdgeInset.left + self.segmentEdgeInset.right;
self.segmentWidth = MAX(imageWidth, self.segmentWidth);
}
}
//segmentCtrl是图文类型 segmentWidth是fix类型
else if (self.type == HMSegmentedControlTypeTextImages && self.segmentWidthStyle == HMSegmentedControlSegmentWidthStyleFixed){
//仅用title来算segment宽,忽略image。略
}
//segmentCtrl是图文类型 segmentWidth是动态类型
else if (self.type == HMSegmentedControlTypeTextImages && HMSegmentedControlSegmentWidthStyleDynamic) {
.....略
}
self.scrollView.scrollEnabled = self.isUserDraggable;//default yes
self.scrollView.contentSize = CGSizeMake([self totalSegmentedControlWidth], self.frame.size.height);
}
- willMoveToSuperview:当自己重写一个UIView的时候有可能用到这个方法,当本视图的父类视图改变的时候,系统会自动的执行这个方法.
- (void)willMoveToSuperview:(UIView *)newSuperview {
// Control is being removed
if (newSuperview == nil)
return;
if (self.sectionTitles || self.sectionImages) {
[self updateSegmentsRects];
}
}
5.drawing绘图
绘制一些相关图层,文本图层、图片图层、竖分割线图层、背景图层、indicator图层(条纹、箭头、方块类型各有图层)。这些图层添加到scrollview(实现segmentCtrl的主体控件)图层上。
- (void)drawRect:(CGRect)rect {
[self.backgroundColor setFill];
UIRectFill([self bounds]);//填充颜色
self.selectionIndicatorArrowLayer.backgroundColor = self.selectionIndicatorColor.CGColor;
self.selectionIndicatorStripLayer.backgroundColor = self.selectionIndicatorColor.CGColor;
self.selectionIndicatorBoxLayer.backgroundColor = self.selectionIndicatorColor.CGColor;
self.selectionIndicatorBoxLayer.borderColor = self.selectionIndicatorColor.CGColor;
//重绘之前把所有子图层先清除,避免重复添加图层
self.scrollView.layer.sublayers = nil;
CGRect oldRect = rect;
if (self.type == HMSegmentedControlTypeText) {//文本类型
[self.sectionTitles enumerateObjectsUsingBlock:^(id titleString, NSUInteger idx, BOOL *stop) {
CGFloat stringWidth = 0;
CGFloat stringHeight = 0;
CGSize size = [self measureTitleAtIndex:idx];//计算文本size
stringWidth = size.width;
stringHeight = size.height;
CGRect rectDiv, fullRect;
// Text inside the CATextLayer will appear blurry unless the rect values are rounded
BOOL locationUp = (self.selectionIndicatorLocation == HMSegmentedControlSelectionIndicatorLocationUp);
BOOL selectionStyleNotBox = (self.selectionStyle != HMSegmentedControlSelectionStyleBox);
//文本y值在segment中的位置和 segment的indicator是否在上位、indicator是否是box类型有关
CGFloat y = roundf((CGRectGetHeight(self.frame) - selectionStyleNotBox * self.selectionIndicatorHeight) / 2 - stringHeight / 2 + self.selectionIndicatorHeight * locationUp);
CGRect rect;
//宽度为fix
if (self.segmentWidthStyle == HMSegmentedControlSegmentWidthStyleFixed) {
//文本rect
rect = CGRectMake((self.segmentWidth * idx) + (self.segmentWidth - stringWidth) / 2, y, stringWidth, stringHeight);
//竖分割线rect
rectDiv = CGRectMake((self.segmentWidth * idx) - (self.verticalDividerWidth / 2), self.selectionIndicatorHeight * 2, self.verticalDividerWidth, self.frame.size.height - (self.selectionIndicatorHeight * 4));
//背景rect
fullRect = CGRectMake(self.segmentWidth * idx, 0, self.segmentWidth, oldRect.size.height);
}
//宽度为dynamic
else if (self.segmentWidthStyle == HMSegmentedControlSegmentWidthStyleDynamic) {
//轮询宽度数组去计算segment的x值
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];//segment的宽,含inset
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));
CATextLayer *titleLayer = [CATextLayer layer];
titleLayer.frame = rect;
titleLayer.alignmentMode = kCAAlignmentCenter;
titleLayer.truncationMode = kCATruncationEnd;
titleLayer.string = [self attributedTitleAtIndex:idx];
titleLayer.contentsScale = [[UIScreen mainScreen] scale];
[self.scrollView.layer addSublayer:titleLayer];
// 竖分割线图层
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];//背景和边沿图层
}];
}
......
}
6.交互
- HMSegmentedControl重写
touchesEnded: withEvent:
方法。HMScrollView中重写的该方法(见1)把touch事件交给下一个响应者来处理,也即是交给HMSegmentedControl来处理。
计算手指松开时触摸的是哪个segment,并做相应的行为处理。
//参数touches表示触摸产生的所有UITouch对象,而event表示特定的事件
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint touchLocation = [touch locationInView:self];//表示触摸在参数view这个视图上的位置,这里返回的位置是针对参数view的坐标系的。
//手指松开时触摸的是哪个segment
if (CGRectContainsPoint(self.bounds, touchLocation)) {
NSInteger segment = 0;
if (self.segmentWidthStyle == HMSegmentedControlSegmentWidthStyleFixed) {
segment = (touchLocation.x + self.scrollView.contentOffset.x) / self.segmentWidth;
} else if (self.segmentWidthStyle == HMSegmentedControlSegmentWidthStyleDynamic) {
CGFloat widthLeft = (touchLocation.x + self.scrollView.contentOffset.x);
for (NSNumber *width in self.segmentWidthsArray) {
//轮询做减法直到不能减为止,就得到当前触摸到得那个segment
widthLeft = widthLeft - [width floatValue];
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];
}
//如果这个segment之前已经选中了,不做处理。
if (segment != self.selectedSegmentIndex && segment < sectionsCount) {
if (self.isTouchEnabled)
[self setSelectedSegmentIndex:segment animated:self.shouldAnimateUserSelection notify:YES];
}
}
}
2.index change
以源代码中的例子为例:
第一种情况:手指点击上面的segment改变index,scrollview随之相应滑动切换。调用HMSegmentedControl重写的touchesEnded:withEvent:
->调用- (void)setSelectedSegmentIndex:(NSUInteger)index animated:(BOOL)animated notify:(BOOL)notify
,notify参数直接传入yes,-> 调用notifyForSegmentChangeToIndex:
。
//这个方法主要实现改变indicator位移。参数notify:是否通知scrollView做相应的处理(以源码中的例子为例)
- (void)setSelectedSegmentIndex:(NSUInteger)index animated:(BOOL)animated notify:(BOOL)notify {
_selectedSegmentIndex = index;//设置index
[self setNeedsDisplay];//通知重绘
if (index == HMSegmentedControlNoSegment) {
[self.selectionIndicatorArrowLayer removeFromSuperlayer];
[self.selectionIndicatorStripLayer removeFromSuperlayer];
[self.selectionIndicatorBoxLayer removeFromSuperlayer];
} else {
[self scrollToSelectedSegmentIndex:animated];
if (animated) {
//如果indicator图层没有添加到父图层上,意味着没有index选中。把indicator图层添加上,不带动画
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 (notify)
[self notifyForSegmentChangeToIndex:index];
//执行indicator位移动画
// Restore CALayer animations
self.selectionIndicatorArrowLayer.actions = nil;
......
[CATransaction begin];
[CATransaction setAnimationDuration:0.15f];
[CATransaction setAnimationTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]];
[self setArrowFrame];
self.selectionIndicatorBoxLayer.frame = [self frameForSelectionIndicator];//arrow类型才对?
......
[CATransaction commit];
}
//animated = NO,没有动画
else {
//直接setframe改变位移
NSMutableDictionary *newActions = [[NSMutableDictionary alloc] initWithObjectsAndKeys:[NSNull null], @"position", [NSNull null], @"bounds", nil];
self.selectionIndicatorArrowLayer.actions = newActions;
[self setArrowFrame];
......
if (notify)
[self notifyForSegmentChangeToIndex:index];
}
}
}
//因手指点击segment(而不是滑动segmentcontrol下方的scrollview),index改变而发送通知(notify)通知vc要执行某些动作,比如要人为手动地去改变scrollview的可视区域(setContentOffset:animated: 或者scrollRectToVisible:animated:)
- (void)notifyForSegmentChangeToIndex:(NSInteger)index {
if (self.superview)
[self sendActionsForControlEvents:UIControlEventValueChanged];
//如果设计了一个自定义控件类(UIControl),可以使用sendActionsForControlEvent方法,为基本的UIControl事件或自己的自定义事件发送通知。发送与指定类型相关的所有行为消息。我们可以在任意位置(包括控件内部和外部)调用控件的这个方法来发送参数controlEvents指定的消息。(见viewdidload中与UIControlEventValueChange相关的addtarget事件)
if (self.indexChangeBlock)
self.indexChangeBlock(index);
}
用到的两种消息传递方法:
-
[self sendActionsForControlEvents:UIControlEventValueChanged];
- (void)sendActionsForControlEvents:(UIControlEvents)controlEvents
如果设计了一个自定义控件类(UIControl,HMSegmentedControl就是继承自UIControl),可以使用sendActionsForControlEvent方法,为基本的UIControl事件或自己的自定义事件发送通知。发送与指定类型相关的所有行为消息。我们可以在任意位置(包括控件内部和外部)调用控件的这个方法来发送参数controlEvents指定的消息。
在源代码例子里面,有两个与UIControlEventValueChange事件:当在执行[self sendActionsForControlEvents:UIControlEventValueChanged];
时,给vc中的UIControlEventValueChange 事件发送通知,segmentedControlChangedValue:
会被调用
[segmentedControl1 addTarget:self action:@selector(segmentedControlChangedValue:) forControlEvents:UIControlEventValueChanged];
[segmentedControl2 addTarget:self action:@selector(segmentedControlChangedValue:) forControlEvents:UIControlEventValueChanged];
- (void)segmentedControlChangedValue:(HMSegmentedControl *)segmentedControl {
NSLog(@"Selected index %ld (via UIControlEventValueChanged)", (long)segmentedControl.selectedSegmentIndex);
}
- indexChangeBlock
ViewController中对indexChangeBlock赋值。
__weak typeof(self) weakSelf = self;
[self.segmentedControl4 setIndexChangeBlock:^(NSInteger index) {
[weakSelf.scrollView scrollRectToVisible:CGRectMake(viewWidth * index, 0, viewWidth, 200) animated:YES];
}];
当self.indexChangeBlock(index);
时,“通知”vc改变scrollview的scrollRectToVisible:可视区域。
- 这两种消息传递方法可以相互替代。不过也可以用代理来实现。
Alternativly, you could use
addTarget:action:forControlEvents:
第二种情况:手指滑动HMSegmentedControl下方的scrollVIew。HMSegmentedControl要根据scrollview滑动的情况来改变index。
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
CGFloat pageWidth = scrollView.frame.size.width;
NSInteger page = scrollView.contentOffset.x / pageWidth;
[self.segmentedControl4 setSelectedSegmentIndex:page animated:YES];
}
//在scrollview代理方法中会调用到这个方法。
- (void)setSelectedSegmentIndex:(NSUInteger)index animated:(BOOL)animated {
//notify参数直接传值为no,不会调用到notifyForSegmentChangeToIndex:方法,即只会改变indicator位置
[self setSelectedSegmentIndex:index animated:animated notify:NO];
}
7.其他
1.- (void)scrollToSelectedSegmentIndex:(BOOL)animated
segment栏目太多,一屏显示不全时需要滚动。一些坐标的计算,没啥好说的。
2.scrollRectToVisible: animated:
[self.scrollView scrollRectToVisible:rectToScrollTo animated:animated];将scrollView坐标系内的一块指定区域移到scrollView的窗口中(centerX),如果这部分已经存在于窗口中,则什么也不做。
3.vc中 有一行代码self.edgesForExtendedLayout = UIRectEdgeNone;
http://www.jianshu.com/p/c0b8c5f131a0