0.一些準(zhǔn)備
typedef void (^IndexChangeBlock)(NSInteger index);//block的typedef 無返回值,參數(shù)nsinterger 雕沿,新別名IndexChangeBlock
typedef NSAttributedString *(^HMTitleFormatterBlock)(HMSegmentedControl *segmentedControl, NSString *title, NSUInteger index, BOOL selected);//返回值NSAttributedString類型,是一種帶有屬性的字符串
當(dāng)改變selected index時執(zhí)行的block车猬,也可以使用addTarget:action:forControlEvents:方式來替代block的執(zhí)行:
@property (nonatomic, copy) IndexChangeBlock indexChangeBlock;
用來設(shè)置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類型時的寬度數(shù)組
@property (nonatomic, readwrite) NSArray *segmentWidthsArray;
//HMScrollView繼承自UIscrollview跺株,是實現(xiàn)HMSegmentedControl的主體控件
@property (nonatomic, strong) HMScrollView *scrollView;
另外复濒,
HMSegmentedControl繼承自UIControl類
@interface HMSegmentedControl : UIControl
1.HMScrollView
HMScrollView是實現(xiàn)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的設(shè)計:自己不處理touches事件,是把touches事件轉(zhuǎn)發(fā)給下一個響應(yīng)者(父視圖 即self)來處理乒省。因為scrollview會“屏蔽”掉下個響應(yīng)者的touches事件巧颈,即單純的[super touches...]不能把事件轉(zhuǎn)發(fā)給下個響應(yīng)者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 方法來做統(tǒng)一的初始化.
//segmentControl內(nèi)容是文字類型
- (id)initWithSectionTitles:(NSArray *)sectiontitles {
self = [self initWithFrame:CGRectZero];//之后再通過實例對象 另外設(shè)置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是否可以滑動(當(dāng)選項過多時會有左右滑動的需要)
self.touchEnabled = YES;//segment是否可以點擊
self.verticalDividerEnabled = NO;//segment之間是否有豎分割線
self.shouldAnimateUserSelection = YES;//切換segment時indicator變化是否有動畫
self.contentMode = UIViewContentModeRedraw;//將在每次設(shè)置或更改frame的時候自動調(diào)用drawRect:
}
如果你是直接基于 frame 來布局的袖扛,你應(yīng)該確保在初始化的時候只添加視圖砸泛,而不去設(shè)置它們的frame,把設(shè)置子視圖 frame 的過程全部放到 layoutSubviews 方法里.如果你是基于 Auto Layout 約束來進行布局蛆封,那么可以在 commonInit 調(diào)用的時候就把約束添加上去唇礁,不要重寫 layoutSubviews 方法,因為這種情況下它的默認實現(xiàn)就是根據(jù)約束來計算 frame惨篱。
-
通過設(shè)置contentMode屬性值為UIViewContentModeRedraw盏筐。那么將在每次設(shè)置或更改frame的時候自動調(diào)用drawRect:。
其余UIViewContentMode(部分舉例):
UIViewContentMode
3.segment上的文本字符串相關(guān)
1.計算文本字符串的size
- (CGSize)measureTitleAtIndex:(NSUInteger)index {
id title = self.sectionTitles[index];
CGSize size = CGSizeZero;
//該segment是否被選中砸讳?
BOOL selected = (index == self.selectedSegmentIndex) ? YES : NO;//selectedSegmentIndex 初始化的時候設(shè)為0
if ([title isKindOfClass:[NSString class]] && !self.titleFormatter) {//titleFormatter文本外觀樣式琢融。為空 執(zhí)行
//是否已經(jīng)選中?分別對應(yīng)不同的默認文本外觀
NSDictionary *titleAttrs = selected ? [self resultingSelectedTitleTextAttributes] : [self resultingTitleTextAttributes];
size = [(NSString *)title sizeWithAttributes:titleAttrs];
} else if ([title isKindOfClass:[NSString class]] && self.titleFormatter) {//titleFormatter非空(已設(shè)置了樣式)
size = [self.titleFormatter(self, title, index, selected) size];//使用設(shè)置的樣式后獲取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;// 將矩形值轉(zhuǎn)變成整數(shù)簿寂,得到一個最小的矩形
}
//非選中狀態(tài)下的文本樣式
- (NSDictionary *)resultingTitleTextAttributes {
NSDictionary *defaults = @{
NSFontAttributeName : [UIFont systemFontOfSize:19.0f],
NSForegroundColorAttributeName : [UIColor blackColor],
};
NSMutableDictionary *resultingAttrs = [NSMutableDictionary dictionaryWithDictionary:defaults];
if (self.titleTextAttributes) {//由外部設(shè)置titleTextAttributes
//addEntriesFromDictionary拼接字典漾抬,原字典已有的相同“鍵”,覆蓋對應(yīng)的鍵值對
[resultingAttrs addEntriesFromDictionary:self.titleTextAttributes];
}
return [resultingAttrs copy];
}
//選中狀態(tài)下的文本樣式
- (NSDictionary *)resultingSelectedTitleTextAttributes {
NSMutableDictionary *resultingAttrs = [NSMutableDictionary dictionaryWithDictionary:[self resultingTitleTextAttributes]];
if (self.selectedTitleTextAttributes) {
[resultingAttrs addEntriesFromDictionary:self.selectedTitleTextAttributes];
}
return [resultingAttrs copy];
}
- addEntriesFromDictionary:方法常遂,拼接字典纳令,如果原字典和新字典有相同“鍵”,用新字典覆蓋對應(yīng)的鍵值對。
- NSAttributedString叫做富文本平绩,是一種帶有屬性的字符串坤按,通過它可以輕松的在一個字符串中表現(xiàn)出多種字體、字號馒过、字體大小等各不相同的風(fēng)格臭脓。
簡單使用,舉例:
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對應(yīng)的文本它的帶屬性的字符串
- (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標(biāo)上一個需要被重新布局的標(biāo)記窘奏,在系統(tǒng)runloop的下一個周期自動調(diào)用layoutSubviews嘹锁。layoutSubviews的調(diào)用時機:
其他相關(guān)方法:
1.layoutSubviews唯一調(diào)用到的方法:
該方法用于更新scrollview(HMScrollView)控件的一些屬性contentInset、frame着裹、scrollEnabled领猾、content size,主要是frame和content size骇扇。以及計算每個segment的寬度摔竿。
//更新scrollview的一些屬性contentInset、frame少孝、scrollEnabled继低、contentsize
- (void)updateSegmentsRects {
self.scrollView.contentInset = UIEdgeInsetsZero;//scrollview的屬性設(shè)置有部分放到了commoninit
self.scrollView.frame = CGRectMake(0, 0, CGRectGetWidth(self.frame), CGRectGetHeight(self.frame));
if ([self sectionCount] > 0) {//sectionCount:方法,計算segment個數(shù)
//計算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) {
//數(shù)組存放每個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是動態(tài)類型
else if (self.type == HMSegmentedControlTypeTextImages && HMSegmentedControlSegmentWidthStyleDynamic) {
.....略
}
self.scrollView.scrollEnabled = self.isUserDraggable;//default yes
self.scrollView.contentSize = CGSizeMake([self totalSegmentedControlWidth], self.frame.size.height);
}
- willMoveToSuperview:當(dāng)自己重寫一個UIView的時候有可能用到這個方法,當(dāng)本視圖的父類視圖改變的時候,系統(tǒng)會自動的執(zhí)行這個方法.
- (void)willMoveToSuperview:(UIView *)newSuperview {
// Control is being removed
if (newSuperview == nil)
return;
if (self.sectionTitles || self.sectionImages) {
[self updateSegmentsRects];
}
}
5.drawing繪圖
繪制一些相關(guān)圖層狐树,文本圖層焙压、圖片圖層、豎分割線圖層褪迟、背景圖層冗恨、indicator圖層(條紋答憔、箭頭味赃、方塊類型各有圖層)。這些圖層添加到scrollview(實現(xiàn)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;
//重繪之前把所有子圖層先清除心俗,避免重復(fù)添加圖層
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類型有關(guān)
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) {
//輪詢寬度數(shù)組去計算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事件交給下一個響應(yīng)者來處理揪利,也即是交給HMSegmentedControl來處理。
計算手指松開時觸摸的是哪個segment狠持,并做相應(yīng)的行為處理疟位。
//參數(shù)touches表示觸摸產(chǎn)生的所有UITouch對象,而event表示特定的事件
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint touchLocation = [touch locationInView:self];//表示觸摸在參數(shù)view這個視圖上的位置,這里返回的位置是針對參數(shù)view的坐標(biāo)系的喘垂。
//手指松開時觸摸的是哪個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) {
//輪詢做減法直到不能減為止甜刻,就得到當(dāng)前觸摸到得那個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之前已經(jīng)選中了,不做處理正勒。
if (segment != self.selectedSegmentIndex && segment < sectionsCount) {
if (self.isTouchEnabled)
[self setSelectedSegmentIndex:segment animated:self.shouldAnimateUserSelection notify:YES];
}
}
}
2.index change
以源代碼中的例子為例:
第一種情況:手指點擊上面的segment改變index得院,scrollview隨之相應(yīng)滑動切換。調(diào)用HMSegmentedControl重寫的touchesEnded:withEvent:
->調(diào)用- (void)setSelectedSegmentIndex:(NSUInteger)index animated:(BOOL)animated notify:(BOOL)notify
章贞,notify參數(shù)直接傳入yes祥绞,-> 調(diào)用notifyForSegmentChangeToIndex:
。
//這個方法主要實現(xiàn)改變indicator位移鸭限。參數(shù)notify:是否通知scrollView做相應(yīng)的處理(以源碼中的例子為例)
- (void)setSelectedSegmentIndex:(NSUInteger)index animated:(BOOL)animated notify:(BOOL)notify {
_selectedSegmentIndex = index;//設(shè)置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];
//執(zhí)行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改變而發(fā)送通知(notify)通知vc要執(zhí)行某些動作喧枷,比如要人為手動地去改變scrollview的可視區(qū)域(setContentOffset:animated: 或者scrollRectToVisible:animated:)
- (void)notifyForSegmentChangeToIndex:(NSInteger)index {
if (self.superview)
[self sendActionsForControlEvents:UIControlEventValueChanged];
//如果設(shè)計了一個自定義控件類(UIControl)虹统,可以使用sendActionsForControlEvent方法,為基本的UIControl事件或自己的自定義事件發(fā)送通知隧甚。發(fā)送與指定類型相關(guān)的所有行為消息车荔。我們可以在任意位置(包括控件內(nèi)部和外部)調(diào)用控件的這個方法來發(fā)送參數(shù)controlEvents指定的消息。(見viewdidload中與UIControlEventValueChange相關(guān)的addtarget事件)
if (self.indexChangeBlock)
self.indexChangeBlock(index);
}
用到的兩種消息傳遞方法:
-
[self sendActionsForControlEvents:UIControlEventValueChanged];
- (void)sendActionsForControlEvents:(UIControlEvents)controlEvents
如果設(shè)計了一個自定義控件類(UIControl戚扳,HMSegmentedControl就是繼承自UIControl)忧便,可以使用sendActionsForControlEvent方法,為基本的UIControl事件或自己的自定義事件發(fā)送通知帽借。發(fā)送與指定類型相關(guān)的所有行為消息珠增。我們可以在任意位置(包括控件內(nèi)部和外部)調(diào)用控件的這個方法來發(fā)送參數(shù)controlEvents指定的消息。
在源代碼例子里面砍艾,有兩個與UIControlEventValueChange事件:當(dāng)在執(zhí)行[self sendActionsForControlEvents:UIControlEventValueChanged];
時蒂教,給vc中的UIControlEventValueChange 事件發(fā)送通知,segmentedControlChangedValue:
會被調(diào)用
[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];
}];
當(dāng)self.indexChangeBlock(index);
時凝垛,“通知”vc改變scrollview的scrollRectToVisible:可視區(qū)域懊悯。
- 這兩種消息傳遞方法可以相互替代。不過也可以用代理來實現(xiàn)梦皮。
Alternativly, you could use
addTarget:action:forControlEvents:
第二種情況:手指滑動HMSegmentedControl下方的scrollVIew炭分。HMSegmentedControl要根據(jù)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代理方法中會調(diào)用到這個方法剑肯。
- (void)setSelectedSegmentIndex:(NSUInteger)index animated:(BOOL)animated {
//notify參數(shù)直接傳值為no捧毛,不會調(diào)用到notifyForSegmentChangeToIndex:方法,即只會改變indicator位置
[self setSelectedSegmentIndex:index animated:animated notify:NO];
}
7.其他
1.- (void)scrollToSelectedSegmentIndex:(BOOL)animated
segment欄目太多让网,一屏顯示不全時需要滾動岖妄。一些坐標(biāo)的計算,沒啥好說的寂祥。
2.scrollRectToVisible: animated:
[self.scrollView scrollRectToVisible:rectToScrollTo animated:animated];將scrollView坐標(biāo)系內(nèi)的一塊指定區(qū)域移到scrollView的窗口中(centerX)荐虐,如果這部分已經(jīng)存在于窗口中,則什么也不做丸凭。
3.vc中 有一行代碼self.edgesForExtendedLayout = UIRectEdgeNone;
http://www.reibang.com/p/c0b8c5f131a0